/*******************************************************************************
 * Copyright (c) 2000, 2010 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.gef.editpolicies;

import java.util.List;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.OrderedLayout;

import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.requests.ChangeBoundsRequest;

/**
 * A LayoutEditPolicy for use with <code>LayoutManagers</code> that take no
 * constraints. Such layout managers typically position children in <x,y>
 * coordinates based on their order in
 * {@link org.eclipse.draw2d.IFigure#getChildren() getChildren()}. Therefore,
 * this EditPolicy must perform the inverse mapping. Given a mouse location from
 * the User, the policy must determine the index at which the child[ren] should
 * be added/created.
 * 
 * @author hudsonr
 * @since 2.0
 */
public abstract class OrderedLayoutEditPolicy extends LayoutEditPolicy {

	/**
	 * Returns the <code>Command</code> to add the specified child after a
	 * reference <code>EditPart</code>. If the reference is <code>null</code>,
	 * the child should be added as the first child.
	 * 
	 * @param child
	 *            the child being added
	 * @param after
	 *            <code>null</code> or a reference EditPart
	 * @return a Command to add the child
	 */
	protected abstract Command createAddCommand(EditPart child, EditPart after);

	/**
	 * Since Ordered layouts generally don't use constraints, a
	 * {@link NonResizableEditPolicy} is used by default for children.
	 * Subclasses may override this method to supply a different EditPolicy.
	 * 
	 * @see org.eclipse.gef.editpolicies.LayoutEditPolicy#createChildEditPolicy(EditPart)
	 */
	protected EditPolicy createChildEditPolicy(EditPart child) {
		return new NonResizableEditPolicy();
	}

	/**
	 * Returns the <code>Command</code> to move the specified child before the
	 * given reference <code>EditPart</code>. If the reference is
	 * <code>null</code>, the child should be moved in front of all children.
	 * <P>
	 * A move is a change in the order of the children, which indirectly causes
	 * a change in location on the screen.
	 * 
	 * @param child
	 *            the child being moved
	 * @param after
	 *            <code>null</code> or the EditPart that should be after (or to
	 *            the right of) the child being moved
	 * @return a Command to move the child
	 */
	protected abstract Command createMoveChildCommand(EditPart child,
			EditPart after);

	/**
	 * This method is overridden from the superclass to calculate the
	 * <i>index</i> at which the children should be added. The index is
	 * determined by finding a reference EditPart, and adding the new child[ren]
	 * <em>after</em> that reference part. <code>null</code> is used to indicate
	 * that the child[ren] should be added at the beginning.
	 * <P>
	 * Subclasses must override {@link #createAddCommand(EditPart, EditPart)},
	 * and should not override this method.
	 * 
	 * @see org.eclipse.gef.editpolicies.LayoutEditPolicy#getAddCommand(Request)
	 */
	protected Command getAddCommand(Request req) {
		ChangeBoundsRequest request = (ChangeBoundsRequest) req;
		List editParts = request.getEditParts();
		CompoundCommand command = new CompoundCommand();
		for (int i = 0; i < editParts.size(); i++) {
			EditPart child = (EditPart) editParts.get(i);
			command.add(createAddCommand(child, getInsertionReference(request)));
		}
		return command.unwrap();
	}

	/**
	 * Calculates a <i>reference</i> <code>EditPart</code> using the specified
	 * <code>Request</code>. The EditPart returned is used to mark the index
	 * coming <em>after</em> that EditPart. <code>null</code> is used to
	 * indicate the index that comes after <em>no</em> EditPart, that is, it
	 * indicates the very last index.
	 * 
	 * @param request
	 *            the Request
	 * @return <code>null</code> or a reference EditPart
	 */
	protected abstract EditPart getInsertionReference(Request request);

	/**
	 * A move is interpreted here as a change in order of the children. This
	 * method obtains the proper index, and then calls
	 * {@link #createMoveChildCommand(EditPart, EditPart)}, which subclasses
	 * must implement. Subclasses should not override this method.
	 * 
	 * @see LayoutEditPolicy#getMoveChildrenCommand(Request)
	 */
	protected Command getMoveChildrenCommand(Request request) {
		CompoundCommand command = new CompoundCommand();
		List editParts = ((ChangeBoundsRequest) request).getEditParts();

		EditPart insertionReference = getInsertionReference(request);
		for (int i = 0; i < editParts.size(); i++) {
			EditPart child = (EditPart) editParts.get(i);
			command.add(createMoveChildCommand(child, insertionReference));
		}
		return command.unwrap();
	}

	/**
	 * Returns whether the layout container's layout manager has a horizontal
	 * orientation or not.
	 * 
	 * @return <code>true</code> if the layout container's layout manager has a
	 *         horizontal orientation, <code>false</code> otherwise
	 * @since 3.7
	 */
	protected boolean isLayoutHorizontal() {
		IFigure figure = getLayoutContainer();
		return ((OrderedLayout) figure.getLayoutManager()).isHorizontal();
	}

}
