package org.jgraph.example;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;

import org.jgraph.graph.AbstractCellView;
import org.jgraph.graph.CellView;
import org.jgraph.graph.GraphConstants;
import org.jgraph.graph.GraphLayoutCache;
import org.jgraph.graph.VertexView;

/**
 * Vertex view that supports visual vertex nesting to show inclusion edges in a
 * compound graph. Differs from a selection group view as follows:
 * <ul>
 * <li>Bounds of a compound vertex view with child views are not determined
 * directly by the child views' bounds, but rather are independently settable
 * like the bounds of a leaf vertex.</li>
 * <li>Scaling a parent compound vertex view does not scale its child views,
 * although translating a parent compound vertex view does translate its child
 * views.</li>
 * </ul>
 * Like any {@link VertexView}, the bounds of a compound vertex view are forced
 * to always remain large enough to enclose its child views. Otherwise, no
 * restrictions are placed on the bounds.
 * 
 * @author J. Pulley
 */
public class CompoundVertexView extends VertexView {

	/**
	 * Initializes a new view for a compound vertex.
	 */
	public CompoundVertexView() {
	}

	/**
	 * Initializes a new view for a compound vertex with the specified model
	 * object.
	 * 
	 * @param cell
	 *            model object
	 */
	public CompoundVertexView(Object cell) {
		super(cell);
	}

	/**
	 * Retrieve this view's bounds from the view's attributes.
	 * 
	 * @return view's bounds
	 */
	public Rectangle2D getBounds() {
		return GraphConstants.getBounds(getAllAttributes());
	}

	/**
	 * Set this view's bounds in the view's attributes. If the new bounds do not
	 * completely enclose any child vertices, the new bounds are set to the
	 * {@link Rectangle2D#createUnion(Rectangle2D) union} of the child vertices'
	 * bounds and the argument.
	 * 
	 * @param newBounds
	 *            new bounds
	 */
	public void setBounds(Rectangle2D newBounds) {
		GraphConstants.setBounds(getAllAttributes(), newBounds);
		checkChildBounds();
	}

	/**
	 * Update attributes for this view and indicate to the parent this child has
	 * been updated.
	 */
	public void update(GraphLayoutCache cache) {
		super.update(cache);
		checkChildBounds();
	}

	/**
	 * Translate this view and all child views by <code>dx, dy</code>.
	 * 
	 * @param dx
	 *            x-axis translation
	 * @param dy
	 *            y-axis translation
	 */
	public void translate(double dx, double dy) {
		getAllAttributes().translate(dx, dy);
		int moveableAxis = GraphConstants.getMoveableAxis(getAllAttributes());
		if (moveableAxis == GraphConstants.X_AXIS) {
			dy = 0;
		} else if (moveableAxis == GraphConstants.Y_AXIS) {
			dx = 0;
		}
		Iterator it = childViews.iterator();
		while (it.hasNext()) {
			Object view = it.next();
			if (view instanceof AbstractCellView) {
				AbstractCellView child = (AbstractCellView) view;
				child.translate(dx, dy);
			}
		}
	}

	/**
	 * Scale this view by <code>sx</code> and <code>sy</code>, relative to
	 * <code>origin</code>. Child views are not scaled.
	 * 
	 * @param sx
	 *            x scaling factor
	 * @param sy
	 *            y scaling factor
	 */
	public void scale(double sx, double sy, Point2D origin) {
		getAllAttributes().scale(sx, sy, origin);
		checkChildBounds();
	}

	/**
	 * If this view's current bounds do not completely enclose all child vertex
	 * views, sets this view's bounds to the union of the current bounds and the
	 * childrens' bounds.
	 */
	private void checkChildBounds() {
		if (!isLeaf()) {
			Rectangle2D bounds = GraphConstants.getBounds(getAllAttributes());
			if (bounds == null) {
				bounds = new Rectangle2D.Double();
			}
			CellView[] childViewArray = (CellView[]) childViews
					.toArray(new CellView[0]);
			Rectangle2D childBounds = AbstractCellView
					.getBounds(childViewArray);
			if (!bounds.contains(childBounds)) {
				bounds = bounds.createUnion(childBounds);
				GraphConstants.setBounds(getAllAttributes(), bounds);
			}
		}
	}
}
