/**
 * File:    ScopeLayoutEditPolicy.java
 * Created: 17.12.2005
 *
 *
/*******************************************************************************
 * Copyright (c) 2005-2011
 * University of Augsburg, Germany <www.ds-lab.org>
 *
 * 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 Bauer, Programming distributed Systems Lab, University of Augsburg 
 *    	- initial API and implementation
 *    Christian Saad, Programming distributed Systems Lab, University of Augsburg 
 *    	- added feedback figure support
 *    Marc Blachon, Bull SAS, Grenoble, France
 *      - getting ImageFactory and FigureFactory from factory registry.
 *******************************************************************************/

package org.eclipse.jwt.we.parts.processes.policies;

import java.util.Iterator;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.RectangleFigure;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.IItemLabelProvider;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.LayerConstants;
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.editpolicies.XYLayoutEditPolicy;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jwt.meta.commands.JWTCreateChildCommand;
import org.eclipse.jwt.meta.model.core.CorePackage;
import org.eclipse.jwt.meta.model.core.GraphicalElement;
import org.eclipse.jwt.meta.model.core.NamedElement;
import org.eclipse.jwt.meta.model.core.Package;
import org.eclipse.jwt.meta.model.core.PackageableElement;
import org.eclipse.jwt.meta.model.core.ReferenceableElement;
import org.eclipse.jwt.meta.model.processes.ActivityNode;
import org.eclipse.jwt.meta.model.processes.Guard;
import org.eclipse.jwt.meta.model.processes.ProcessesPackage;
import org.eclipse.jwt.meta.model.processes.Scope;
import org.eclipse.jwt.meta.model.processes.StructuredActivityNode;
import org.eclipse.jwt.we.Plugin;
import org.eclipse.jwt.we.commands.gefEmfAdapter.EmfToGefCommandAdapter;
import org.eclipse.jwt.we.commands.scope.InsertScopeCommand;
import org.eclipse.jwt.we.commands.view.SetGraphicalElementConstraintCommand;
import org.eclipse.jwt.we.editors.preferences.PreferenceReader;
import org.eclipse.jwt.we.figures.internal.IWEFigure;
import org.eclipse.jwt.we.misc.factories.CreateReferenceFactory;
import org.eclipse.jwt.we.misc.factories.TransferFactory;
import org.eclipse.jwt.we.misc.logging.Logger;
import org.eclipse.jwt.we.misc.util.EMFHelper;
import org.eclipse.jwt.we.misc.util.GeneralHelper;
import org.eclipse.jwt.we.misc.views.Views;
import org.eclipse.jwt.we.model.view.Reference;
import org.eclipse.jwt.we.model.view.ViewPackage;

/**
 * EditPolicy for the ScopeEditPart.
 * 
 * @version $Id: ScopeLayoutEditPolicy.java,v 1.15 2009/11/04 17:18:45 chsaad
 *          Exp $
 * @author Markus Bauer (markusbauer@users.sourceforge.net), Programming
 *         distributed Systems Lab, University of Augsburg, Germany,
 *         www.ds-lab.org
 */
public class ScopeLayoutEditPolicy extends XYLayoutEditPolicy
{

	/**
	 * A Logger.
	 */
	private static final Logger logger = Logger
			.getLogger(ScopeLayoutEditPolicy.class);

	/**
	 * A ghost feedback figure.
	 */
	protected IFigure feedbackFigure;


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.gef.editpolicies.LayoutEditPolicy#getCreateCommand(org.eclipse
	 * .gef. requests.CreateRequest)
	 */
	@Override
	protected Command getCreateCommand(CreateRequest request)
	{
		Object childClass = request.getNewObjectType();
		Object model = getHost().getModel();
		Object constraint = getConstraintFor(request);
		EditingDomain editingDomain = (EditingDomain) getHost().getAdapter(
				EditingDomain.class);

		// a feedback figure is moved, so it is centered around the mouse
		// pointer. so the
		// position of the create request must be manipulated in the same
		// manner.
		if (feedbackFigure != null)
		{
			((Rectangle) constraint).x -= feedbackFigure.getBounds().width / 2;
			((Rectangle) constraint).y -= feedbackFigure.getBounds().height / 2;
		}

		// create and return the command(s)
		if (childClass instanceof Class && model instanceof Scope
				&& constraint instanceof Rectangle)
		{
			if (logger.isDebugEnabled())
			{
				logger
						.debug("Creating new " + ((Class) childClass).getSimpleName()); //$NON-NLS-1$
			}

			// ActivityNode
			if (ActivityNode.class.isAssignableFrom((Class) childClass))
			{
				// create and add to scope (which is the container)
				Command creationCommand = new EmfToGefCommandAdapter(
						new JWTCreateChildCommand(editingDomain, model,
								ProcessesPackage.Literals.SCOPE__NODES, request
										.getNewObject()));

				// set graphical constraint
				SetGraphicalElementConstraintCommand graphicalConstraintCommand = new SetGraphicalElementConstraintCommand(
						GeneralHelper.getInstanceForEObject((EObject)model), request,
						(Rectangle) constraint);

				return creationCommand.chain(graphicalConstraintCommand);
			}
			// ReferenceableElement (create new refElem and reference)
			else if (ReferenceableElement.class
					.isAssignableFrom((Class) childClass)
					&& (model instanceof PackageableElement || model instanceof StructuredActivityNode))
			{
				// the package where the refelem is created (standard: root
				// model package)
				Package packageModel = (Package) GeneralHelper.getInstanceForEObject((EObject)model).getModel();
				if (model instanceof PackageableElement)
				{
					// if packelem (e.g. activity), use its parent package
					packageModel = ((PackageableElement) model).getPackage();
				}
				else if (GeneralHelper.getInstanceForEObject((EObject)model) != null)
				{
					// if an activity is displayed, use its parent package
					packageModel = GeneralHelper.getInstanceForEObject((EObject)model)
							.getDisplayedActivityModel().getPackage();
				}

				// create the ReferenceableElement to package of Activity
				Command creationCommand = new EmfToGefCommandAdapter(
						new JWTCreateChildCommand(editingDomain, packageModel,
								CorePackage.Literals.PACKAGE__ELEMENTS, request
										.getNewObject()));

				// create Reference and add to scope
				CreateRequest createReferenceRequest = new CreateRequest();
				createReferenceRequest.setFactory(new CreateReferenceFactory(
						(ReferenceableElement) request.getNewObject()));
				createReferenceRequest.setLocation(request.getLocation());
				createReferenceRequest.setSize(request.getSize());

				Command createReferenceCommand = getCreateCommand(createReferenceRequest);

				return creationCommand.chain(createReferenceCommand);
			}

			// Reference
			else if (Reference.class.isAssignableFrom((Class) childClass))
			{
				Object newObject = request.getNewObject();

				// add the new element
				Command setCommand = new EmfToGefCommandAdapter(SetCommand
						.create(editingDomain, newObject,
								ViewPackage.Literals.REFERENCE__CONTAINED_IN,
								model));

				Command addCommand = new EmfToGefCommandAdapter(AddCommand
						.create(editingDomain, EMFHelper
								.getDiagram(GeneralHelper.getInstanceForEObject((EObject)model)),
								ViewPackage.Literals.DIAGRAM__REFERENCES,
								newObject));

				SetGraphicalElementConstraintCommand graphicalConstraintCommand = new SetGraphicalElementConstraintCommand(
						GeneralHelper.getInstanceForEObject((EObject)model), request,
						(Rectangle) constraint);

				return setCommand.chain(addCommand).chain(
						graphicalConstraintCommand);
			}

			// Scope
			else if (Scope.class.isAssignableFrom((Class) childClass))
			{
				return new InsertScopeCommand(
						GeneralHelper.getInstanceForEObject((EObject)model), editingDomain,
						request, (Scope) model, ((Rectangle) constraint)
								.getLocation());
			}

			// Iterable
			else if (Iterable.class.isAssignableFrom((Class) childClass))
			{
				// collect create commands for each new Object
				CompoundCommand command = new CompoundCommand();
				for (Iterator i = ((Iterable) request.getNewObject())
						.iterator(); i.hasNext();)
				{
					Object requestobject = i.next();
					CreateRequest createRequest = new CreateRequest();

					// if refelement, create ONLY reference (not a copy of the
					// refelement
					if (requestobject instanceof ReferenceableElement)
						createRequest.setFactory(new CreateReferenceFactory(
								(ReferenceableElement) requestobject));
					else
						createRequest.setFactory(new TransferFactory(
								requestobject));

					createRequest.setLocation(request.getLocation());
					createRequest.setSize(request.getSize());
					command.add(getCreateCommand(createRequest));

				}

				return command;
			}
		}

		return UnexecutableCommand.INSTANCE;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @seeorg.eclipse.gef.editpolicies.ConstrainedLayoutEditPolicy#
	 * createChangeConstraintCommand
	 * (org.eclipse.gef.requests.ChangeBoundsRequest, org.eclipse.gef.EditPart,
	 * java.lang.Object)
	 */
	@Override
	protected Command createChangeConstraintCommand(
			ChangeBoundsRequest request, EditPart child, Object constraint)
	{
		Object childModel = child.getModel();

		if (childModel instanceof GraphicalElement
				&& constraint instanceof Rectangle)
		{
			return new SetGraphicalElementConstraintCommand(GeneralHelper.getInstanceForEObject((EObject)childModel), (GraphicalElement) childModel,
					(Rectangle) constraint);
		}

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


	/*
	 * (non-Javadoc)
	 * 
	 * @seeorg.eclipse.gef.editpolicies.ConstrainedLayoutEditPolicy#
	 * createChangeConstraintCommand (org.eclipse.gef.EditPart,
	 * java.lang.Object)
	 */
	@Override
	protected Command createChangeConstraintCommand(EditPart child,
			Object constraint)
	{
		return UnexecutableCommand.INSTANCE;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.gef.editpolicies.LayoutEditPolicy#decorateChild(org.eclipse
	 * .gef.EditPart )
	 */
	@Override
	protected void decorateChild(EditPart child)
	{
		if (child.getEditPolicy(EditPolicy.PRIMARY_DRAG_ROLE) == null)
		{
			super.decorateChild(child);
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.editpolicies.GraphicalEditPolicy#getFeedbackLayer()
	 */
	@Override
	protected IFigure getFeedbackLayer()
	{
		// return a scaled version of the feedback layer to make sure zoom works
		// correctly
		return getLayer(LayerConstants.SCALED_FEEDBACK_LAYER);
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.gef.editpolicies.LayoutEditPolicy#showLayoutTargetFeedback
	 * (org.eclipse .gef.Request)
	 */
	@Override
	protected void showLayoutTargetFeedback(Request request)
	{
		// show feedback figure?
		if (!PreferenceReader.appearanceFeedbackVisible.get())
		{
			return;
		}

		// if the request is a create request, create a feedback figure
		if (request instanceof CreateRequest)
		{
			// create and add the feedback figure
			if (feedbackFigure == null)
			{
				feedbackFigure = createFeedbackFigure(request);
				addFeedback(feedbackFigure);
			}

			// the request is always given in the coordinate system of the main
			// scope.
			// because of this, in order to calculate the position of the
			// feedback figure,
			// the layout policy of the main scope is used (this policy is also
			// used fo
			// structuredactivitynodes)
			EditPartViewer editPartViewer = GeneralHelper.getActiveInstance()
					.getCurrentActivitySheet().getGraphicalViewer();
			ScopeLayoutEditPolicy layoutPolicy = (ScopeLayoutEditPolicy) editPartViewer
					.getContents().getEditPolicy(EditPolicy.LAYOUT_ROLE);

			// calculate the absolute position (using the main scope)
			Rectangle bounds = (Rectangle) layoutPolicy
					.getConstraintFor((CreateRequest) request);
			bounds.setSize(bounds.getSize().union(
					feedbackFigure.getPreferredSize()));
			feedbackFigure.setBounds(bounds);

			// move the feedback figure, so it is centered around the mouse
			// pointer
			feedbackFigure.getBounds().x -= feedbackFigure.getBounds().width / 2;
			feedbackFigure.getBounds().y -= feedbackFigure.getBounds().height / 2;
			feedbackFigure.revalidate();
		}
	}


	/**
	 * Create a ghost feedback figure for a create request.
	 * 
	 * @param request
	 * @return The feedback figure.
	 */
	protected IFigure createFeedbackFigure(Request request)
	{
		IFigure fbFigure = null;

		Object objectType = ((CreateRequest) request).getNewObjectType();
		Object newObject = ((CreateRequest) request).getNewObject();

		// if the object to be created is not an EObject (e.g. dragged from
		// outline view)
		if (!(newObject instanceof EObject))
		{
			return new RectangleFigure();
		}

		// if the object is a reference
		if (newObject instanceof Reference)
		{
			// get the lavel provider of the referenceablelement
			ReferenceableElement refElem = ((Reference) newObject)
					.getReference();
			AdapterFactory adapterFactory = ((AdapterFactoryEditingDomain) AdapterFactoryEditingDomain
					.getEditingDomainFor(refElem)).getAdapterFactory();
			IItemLabelProvider itemLabelProvider = (IItemLabelProvider) adapterFactory
					.adapt(refElem, IItemLabelProvider.class);

			// create a referenceableelement figure with the original values
			fbFigure = Plugin.getInstance().getFactoryRegistry()
					.getFigureFactory()
					.createFigure(ReferenceableElement.class);
			if (fbFigure instanceof IWEFigure)
			{
				((IWEFigure) fbFigure).setName(refElem.getName());
				((IWEFigure) fbFigure).setImageFactory(Plugin.getInstance()
						.getFactoryRegistry().getImageFactory(
								Views.getInstance().getSelectedView()));
				((IWEFigure) fbFigure)
						.setIcon((ImageDescriptor) itemLabelProvider
								.getImage(refElem));
			}
		}
		// if the object is a referenceable element
		else if (newObject instanceof ReferenceableElement)
		{
			// create a standard referenceableelement figure of the
			// corresponding type
			fbFigure = Plugin.getInstance().getFactoryRegistry()
					.getFigureFactory()
					.createFigure(ReferenceableElement.class);
			if (fbFigure instanceof IWEFigure)
			{
				((IWEFigure) fbFigure).setImageFactory(Plugin.getInstance()
						.getFactoryRegistry().getImageFactory(
								Views.getInstance().getSelectedView()));
				((IWEFigure) fbFigure).setIcon(Plugin.getInstance()
						.getFactoryRegistry().getImageFactory(
								Views.getInstance().getSelectedView())
						.createModelTypeImageDescriptor(
								((CreateRequest) request).getNewObjectType()));
			}
		}
		// if the object is a guard
		else if (newObject instanceof Guard)
		{
			// create a standard guard figure
			fbFigure = Plugin.getInstance().getFactoryRegistry()
					.getFigureFactory().createFigure(
							GeneralHelper.getObjectClass(objectType));
		}
		// anything else
		else
		{
			// create a standard type figure
			// fbFigure = FigureFactory.getInstance().createFigure(objectType);
			fbFigure = Plugin.getInstance().getFactoryRegistry()
					.getFigureFactory().createFigure(
							GeneralHelper.getObjectClass(objectType));

			// if the figure is a named element figure, set name and icon
			if (fbFigure instanceof IWEFigure)
			{
				((IWEFigure) fbFigure).setName(((NamedElement) newObject)
						.getName());
				((IWEFigure) fbFigure).setImageFactory(Plugin.getInstance()
						.getFactoryRegistry().getImageFactory(
								Views.getInstance().getSelectedView()));
				((IWEFigure) fbFigure).setIcon(Plugin.getInstance()
						.getFactoryRegistry().getImageFactory(
								Views.getInstance().getSelectedView())
						.createModelTypeImageDescriptor(
								((CreateRequest) request).getNewObjectType()));
			}
		}

		// enable alpha filtering
		if (fbFigure instanceof IWEFigure)
		{
			((IWEFigure) fbFigure).setAlpha(true);
		}

		return fbFigure;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.gef.editpolicies.LayoutEditPolicy#eraseLayoutTargetFeedback
	 * (org.eclipse .gef.Request)
	 */
	@Override
	protected void eraseLayoutTargetFeedback(Request request)
	{
		// remove the feedback figure
		if (feedbackFigure != null)
		{
			removeFeedback(feedbackFigure);
			feedbackFigure = null;
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.gef.editpolicies.XYLayoutEditPolicy#showSizeOnDropFeedback
	 * (org.eclipse .gef.requests.CreateRequest)
	 */
	@Override
	protected void showSizeOnDropFeedback(CreateRequest request)
	{
		// if the user decides to resize the object during creation time by
		// dragging the
		// mouse, the feedback figure must be removed for performance reasons
		// and because
		// it is anyway at the wrong location for drag-inserting (see
		// getConstraintFor).
		removeFeedback(feedbackFigure);
		feedbackFigure = null;

		// call super size
		super.showSizeOnDropFeedback(request);
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.gef.editpolicies.ConstrainedLayoutEditPolicy#getConstraintFor
	 * (org.eclipse .gef.requests.CreateRequest)
	 */
	@Override
	public Object getConstraintFor(CreateRequest request)
	{
		return super.getConstraintFor(request);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.gef.editpolicies.LayoutEditPolicy#createSizeOnDropFeedback
	 * (org.eclipse .gef.requests.CreateRequest)
	 */
	/*
	 * @Override protected IFigure createSizeOnDropFeedback(CreateRequest
	 * createRequest) { logger.enter(); IFigure figure; figure = new
	 * RectangleFigure(); figure.setForegroundColor(ColorConstants.blue);
	 * addFeedback(figure); return figure; }
	 */
}
