package com.mxgraph.layout;

import java.util.ArrayList;
import java.util.List;

import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.view.mxGraph;

public class mxCircleLayout extends mxGraphLayout
{

	/**
	 * Integer specifying the size of the radius. Default is 100.
	 */
	protected double radius;

	/**
	 * Boolean specifying if the circle should be moved to the top,
	 * left corner specified by x0 and y0. Default is false.
	 */
	protected boolean moveCircle = true;

	/**
	 * Integer specifying the left coordinate of the circle.
	 * Default is 0.
	 */
	protected double x0 = 0;

	/**
	 * Integer specifying the top coordinate of the circle.
	 * Default is 0.
	 */
	protected double y0 = 0;

	/**
	 * Specifies if all edge points of traversed edges should be removed.
	 * Default is true.
	 */
	protected boolean resetEdges = false;

	/**
	 *  Specifies if the STYLE_NOEDGESTYLE flag should be set on edges that are
	 * modified by the result. Default is true.
	 */
	protected boolean disableEdgeStyle = true;

	/**
	 * Constructs a new stack layout layout for the specified graph,
	 * spacing, orientation and offset.
	 */
	public mxCircleLayout(mxGraph graph)
	{
		this(graph, 100);
	}

	/**
	 * Constructs a new stack layout layout for the specified graph,
	 * spacing, orientation and offset.
	 */
	public mxCircleLayout(mxGraph graph, double radius)
	{
		super(graph);
		this.radius = radius;
	}

	/**
	 * @return the radius
	 */
	public double getRadius()
	{
		return radius;
	}

	/**
	 * @param radius the radius to set
	 */
	public void setRadius(double radius)
	{
		this.radius = radius;
	}

	/**
	 * @return the moveCircle
	 */
	public boolean isMoveCircle()
	{
		return moveCircle;
	}

	/**
	 * @param moveCircle the moveCircle to set
	 */
	public void setMoveCircle(boolean moveCircle)
	{
		this.moveCircle = moveCircle;
	}

	/**
	 * @return the x0
	 */
	public double getX0()
	{
		return x0;
	}

	/**
	 * @param x0 the x0 to set
	 */
	public void setX0(double x0)
	{
		this.x0 = x0;
	}

	/**
	 * @return the y0
	 */
	public double getY0()
	{
		return y0;
	}

	/**
	 * @param y0 the y0 to set
	 */
	public void setY0(double y0)
	{
		this.y0 = y0;
	}

	/**
	 * @return the resetEdges
	 */
	public boolean isResetEdges()
	{
		return resetEdges;
	}

	/**
	 * @param resetEdges the resetEdges to set
	 */
	public void setResetEdges(boolean resetEdges)
	{
		this.resetEdges = resetEdges;
	}

	/**
	 * @return the disableEdgeStyle
	 */
	public boolean isDisableEdgeStyle()
	{
		return disableEdgeStyle;
	}

	/**
	 * @param disableEdgeStyle the disableEdgeStyle to set
	 */
	public void setDisableEdgeStyle(boolean disableEdgeStyle)
	{
		this.disableEdgeStyle = disableEdgeStyle;
	}

	/*
	 * (non-Javadoc)
	 * @see com.mxgraph.layout.mxIGraphLayout#execute(java.lang.Object)
	 */
	public void execute(Object parent)
	{
		mxIGraphModel model = graph.getModel();

		// Moves the vertices to build a circle. Makes sure the
		// radius is large enough for the vertices to not
		// overlap
		model.beginUpdate();
		try
		{
			// Gets all vertices inside the parent and finds
			// the maximum dimension of the largest vertex
			double max = 0;
			Double top = null;
			Double left = null;
			List<Object> vertices = new ArrayList<Object>();
			int childCount = model.getChildCount(parent);

			for (int i = 0; i < childCount; i++)
			{
				Object cell = model.getChildAt(parent, i);

				if (!isVertexIgnored(cell))
				{
					vertices.add(cell);
					mxRectangle bounds = getVertexBounds(cell);

					if (top == null)
					{
						top = bounds.getY();
					}
					else
					{
						top = Math.min(top, bounds.getY());
					}

					if (left == null)
					{
						left = bounds.getX();
					}
					else
					{
						left = Math.min(left, bounds.getX());
					}

					max = Math.max(max, Math.max(bounds.getWidth(), bounds
							.getHeight()));
				}
				else if (!isEdgeIgnored(cell))
				{
					if (isResetEdges())
					{
						graph.resetEdge(cell);
					}
	
					if (isDisableEdgeStyle())
					{
						setEdgeStyleEnabled(cell, false);
					}
				}
			}

			int vertexCount = vertices.size();
			double r = Math.max(vertexCount * max / Math.PI, radius);

			// Moves the circle to the specified origin
			if (moveCircle)
			{
				top = x0;
				left = y0;
			}

			circle(vertices.toArray(), r, left.doubleValue(), top.doubleValue());
		}
		finally
		{
			model.endUpdate();
		}
	}

	/**
	 * Executes the circular layout for the specified array
	 * of vertices and the given radius.
	 */
	public void circle(Object[] vertices, double r, double left, double top)
	{
		int vertexCount = vertices.length;
		double phi = 2 * Math.PI / vertexCount;

		for (int i = 0; i < vertexCount; i++)
		{
			if (isVertexMovable(vertices[i]))
			{
				setVertexLocation(vertices[i],
						left + r + r * Math.sin(i * phi), top + r + r
								* Math.cos(i * phi));
			}
		}
	}

}
