/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.draw2d;

import java.util.HashMap;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Map;

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;

/**
 * This class implements the {@link org.eclipse.draw2d.LayoutManager} interface using the 
 * XY Layout algorithm. This lays out the components using the layout constraints as 
 * defined by each component.
 */
public class XYLayout
	extends AbstractLayout
{

/** The layout contraints */
protected Map constraints = new HashMap();

/**
 * Calculates and returns the preferred size of the input figure. Since in XYLayout the 
 * location of the child should be preserved, the preferred size would be a region which 
 * would hold all the children of the input figure. If no constraint is set, that child 
 * is ignored for calculation. If width and height are not positive, the preferred 
 * dimensions of the child are taken.
 * 
 * @see AbstractLayout#calculatePreferredSize(IFigure, int, int)
 * @since 2.0
 */
protected Dimension calculatePreferredSize(IFigure f, int wHint, int hHint) {
	Rectangle rect = new Rectangle();
	ListIterator children = f.getChildren().listIterator();
	while (children.hasNext()) {
		IFigure child = (IFigure)children.next();
		Rectangle r = (Rectangle)constraints.get(child);
		if (r == null)
			continue;
		
		if (r.width == -1 || r.height == -1) {
			Dimension preferredSize = child.getPreferredSize(r.width, r.height);
			r = r.getCopy();
			if (r.width == -1)
				r.width = preferredSize.width;
			if (r.height == -1)
				r.height = preferredSize.height;
		}
		rect.union(r);
	}
	Dimension d = rect.getSize();
	Insets insets = f.getInsets();
	return new Dimension(d.width + insets.getWidth(), d.height + insets.getHeight()).
		union(getBorderPreferredSize(f));
}

/**
 * @see LayoutManager#getConstraint(IFigure)
 */
public Object getConstraint(IFigure figure) {
	return constraints.get(figure);
}

/**
 * Returns the origin for the given figure.
 * @param parent the figure whose origin is requested
 * @return the origin
 */
public Point getOrigin(IFigure parent) {
	return parent.getClientArea().getLocation();
}

/**
 * Implements the algorithm to layout the components of the given container figure.
 * Each component is laid out using its own layout constraint specifying its size
 * and position.
 * 
 * @see LayoutManager#layout(IFigure)
 */
public void layout(IFigure parent) {
	Iterator children = parent.getChildren().iterator();
	Point offset = getOrigin(parent);
	IFigure f;
	while (children.hasNext()) {
		f = (IFigure)children.next();
		Rectangle bounds = (Rectangle)getConstraint(f);
		if (bounds == null) continue;

		if (bounds.width == -1 || bounds.height == -1) {
			Dimension preferredSize = f.getPreferredSize(bounds.width, bounds.height);
			bounds = bounds.getCopy();
			if (bounds.width == -1)
				bounds.width = preferredSize.width;
			if (bounds.height == -1)
				bounds.height = preferredSize.height;
		}
		bounds = bounds.getTranslated(offset);
		f.setBounds(bounds);
	}
}

/**
 * @see LayoutManager#remove(IFigure)
 */
public void remove(IFigure figure) {
	super.remove(figure);
	constraints.remove(figure);
}

/**
 * Sets the layout constraint of the given figure. The constraints can only be of type 
 * {@link Rectangle}.
 *
 * @see LayoutManager#setConstraint(IFigure, Object)
 * @since 2.0
 */
public void setConstraint(IFigure figure, Object newConstraint) {
	super.setConstraint(figure, newConstraint);
	if (newConstraint != null)
		constraints.put(figure, newConstraint);
}

}
