/**
 * Copyright (c) 2012 committers of YAKINDU 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:
 * 	Markus Muehlbrandt - initial API and implementation
 * 
 */
package org.yakindu.base.gmf.runtime.treelayout.policies;

import java.util.List;

import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.RoundedRectangle;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.commands.UnexecutableCommand;
import org.eclipse.gef.requests.AlignmentRequest;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.XYLayoutEditPolicy;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewAndElementRequest;
import org.yakindu.base.gmf.runtime.treelayout.TreeLayoutConstraint;
import org.yakindu.base.gmf.runtime.treelayout.TreeLayoutUtil;
import org.yakindu.base.gmf.runtime.treelayout.commands.UpdateAnnotationsOnCreationCommand;
import org.yakindu.base.gmf.runtime.treelayout.commands.UpdateAnnotationsOnMoveCommand;

/**
 * 
 * @author Markus Muehlbrandt
 *
 */
public class TreeLayoutEditPolicy extends XYLayoutEditPolicy {

	private IFigure feedbackFigure;

	@Override
	protected Command getCreateCommand(CreateRequest request) {
		final CompoundCommand cmd = new CompoundCommand();
		cmd.add(super.getCreateCommand(request));
		if (request instanceof CreateViewAndElementRequest) {
			final CreateViewAndElementRequest req = (CreateViewAndElementRequest) request;
			if (shouldUpdateAnnotationsOnCreation(req)) {
				cmd.add(new ICommandProxy(
						new UpdateAnnotationsOnCreationCommand(getHost()
								.getEditingDomain(), req)));
			}
		}
		return cmd;
	}

	protected boolean shouldUpdateAnnotationsOnCreation(
			CreateViewAndElementRequest request) {
		return true;
	}

	@Override
	protected Object getConstraintFor(ChangeBoundsRequest request,
			GraphicalEditPart child) {

		if (request instanceof AlignmentRequest) {
			return super.getConstraintFor(request, child);
		}
		final Rectangle rect = (Rectangle) super.getConstraintFor(request,
				child);
		final Rectangle cons = getCurrentConstraintFor(child);
		final int newTreePosition = TreeLayoutUtil.getNewTreeNodePosition(
				request.getLocation(),
				TreeLayoutUtil.getSiblings((IGraphicalEditPart) child));
		if (cons instanceof TreeLayoutConstraint) {
			final TreeLayoutConstraint treeLayoutConstraint = (TreeLayoutConstraint) cons;
			return new TreeLayoutConstraint(rect,
					treeLayoutConstraint.isRoot(), newTreePosition);
		}
		return new TreeLayoutConstraint(rect, false, newTreePosition);
	}

	@Override
	protected Command createChangeConstraintCommand(EditPart child,
			Object constraint) {
		if (constraint instanceof TreeLayoutConstraint) {
			if (((TreeLayoutConstraint) constraint).isRoot()) {
				return UnexecutableCommand.INSTANCE;
			} else {
				return new ICommandProxy(new UpdateAnnotationsOnMoveCommand(
						getHost().getEditingDomain(),
						(IGraphicalEditPart) child,
						(TreeLayoutConstraint) constraint));
			}

		}
		return super.createChangeConstraintCommand(child, constraint);
	}

	@Override
	protected void showLayoutTargetFeedback(Request request) {
		if (request instanceof ChangeBoundsRequest) {
			final ChangeBoundsRequest changeBoundsRequest = (ChangeBoundsRequest) request;
			if (!changeBoundsRequest.getEditParts().isEmpty()
					&& !(changeBoundsRequest.getEditParts().get(0) instanceof LabelEditPart)) {

				final IGraphicalEditPart editPart = (IGraphicalEditPart) changeBoundsRequest
						.getEditParts().get(0);

				final List<IGraphicalEditPart> siblingList = TreeLayoutUtil
						.getSiblings(editPart);

				if (!siblingList.isEmpty()) {
					final int newTreePosition = TreeLayoutUtil
							.getNewTreeNodePosition(
									changeBoundsRequest.getLocation(),
									TreeLayoutUtil.getSiblings(editPart));
					Point point;
					if (newTreePosition == 0) {
						// top end
						point = getFeedBackFigurePoint(editPart.getFigure(),
								siblingList, 0, 0);
					} else if (newTreePosition == siblingList.size()) {
						// Bottom end
						point = getFeedBackFigurePoint(editPart.getFigure(),
								siblingList, siblingList.size(),
								siblingList.size());
					} else {
						// Between siblings
						point = getFeedBackFigurePoint(editPart.getFigure(),
								siblingList, newTreePosition - 1,
								newTreePosition);
					}

					final Rectangle bounds = getFeedbackFigureBounds();
					siblingList.get(0).getFigure().translateToAbsolute(bounds);
					bounds.setLocation(point);
					getFeedbackFigure().setBounds(bounds);
					getFeedbackFigure().translateToRelative(
							getFeedbackFigure().getBounds());
					getFeedbackLayer().add(getFeedbackFigure());
					getFeedbackLayer().repaint();
				}
			}
		}
	}

	@Override
	protected void eraseLayoutTargetFeedback(Request request) {
		super.eraseLayoutTargetFeedback(request);
		getFeedbackLayer().getChildren().remove(getFeedbackFigure());
		getFeedbackLayer().repaint();
	}

	/**
	 * Calculates FeedbackfigurePoint in absolute coordinates
	 * 
	 * @param childFigure
	 * @param siblingList
	 * @param lowerIndex
	 * @param upperIndex
	 * @return
	 */
	private Point getFeedBackFigurePoint(IFigure childFigure,
			List<IGraphicalEditPart> siblingList, int lowerIndex, int upperIndex) {
		final Point point = new Point();

		IFigure topFigure;
		IFigure bottomFigure;

		if (lowerIndex == siblingList.size()) {
			topFigure = siblingList.get(lowerIndex - 1).getFigure();
			bottomFigure = siblingList.get(upperIndex - 1).getFigure();
		} else {
			topFigure = siblingList.get(lowerIndex).getFigure();
			bottomFigure = siblingList.get(upperIndex).getFigure();
		}

		final Rectangle absTopFigBounds = topFigure.getBounds().getCopy();
		topFigure.translateToAbsolute(absTopFigBounds);

		final Rectangle absBottomFigBounds = bottomFigure.getBounds().getCopy();
		bottomFigure.translateToAbsolute(absBottomFigBounds);

		final Rectangle feedbackFigBounds = getFeedbackFigureBounds();
		topFigure.translateToAbsolute(feedbackFigBounds);

		point.x = absTopFigBounds.x
				- (feedbackFigBounds.width - absTopFigBounds.width) / 2;

		if (lowerIndex == 0 && upperIndex == 0) {
			// Top end
			point.y = absTopFigBounds.y
					+ (absBottomFigBounds.y - absTopFigBounds.y) / 2 - 5;
		} else if (lowerIndex == siblingList.size()
				&& upperIndex == siblingList.size()) {
			// bottom end
			point.y = absTopFigBounds.y
					+ (absBottomFigBounds.y - absTopFigBounds.y) / 2
					+ absBottomFigBounds.height + 5;
		} else {
			point.y = absTopFigBounds.y
					+ (absBottomFigBounds.y - absTopFigBounds.y) / 2
					+ absBottomFigBounds.height / 2;
		}
		return point;
	}

	private IFigure getFeedbackFigure() {
		if (feedbackFigure == null) {
			final RoundedRectangle pl = new RoundedRectangle();
			pl.setForegroundColor(ColorConstants.black);
			pl.setBackgroundColor(ColorConstants.black);
			pl.setBounds(getFeedbackFigureBounds());
			feedbackFigure = pl;
		}
		return feedbackFigure;
	}

	private Rectangle getFeedbackFigureBounds() {
		return new Rectangle(0, 0, 120, 2);
	}

	@Override
	public IGraphicalEditPart getHost() {
		return (IGraphicalEditPart) super.getHost();
	}
}
