/**
 * File:    InsertScopeCopyCommand.java
 * Created: 15.03.2006
 *
 *
/*******************************************************************************
 * 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
 *******************************************************************************/

package org.eclipse.jwt.we.commands.scope;

import java.util.ArrayList;
import java.util.Collection;

import org.eclipse.draw2d.geometry.Point;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.jwt.meta.model.core.Comment;
import org.eclipse.jwt.meta.model.core.GraphicalElement;
import org.eclipse.jwt.meta.model.processes.Activity;
import org.eclipse.jwt.meta.model.processes.ActivityEdge;
import org.eclipse.jwt.meta.model.processes.ActivityNode;
import org.eclipse.jwt.meta.model.processes.ProcessesPackage;
import org.eclipse.jwt.meta.model.processes.Scope;
import org.eclipse.jwt.we.editors.WEEditor;
import org.eclipse.jwt.we.misc.util.EMFHelper;
import org.eclipse.jwt.we.misc.views.LayoutDataManager;
import org.eclipse.jwt.we.model.view.LayoutData;
import org.eclipse.jwt.we.model.view.Reference;
import org.eclipse.jwt.we.model.view.ReferenceEdge;
import org.eclipse.jwt.we.model.view.ViewPackage;

/**
 * Insert a complete copy of a {@link Scope} into another {@link Scope}.
 * 
 * @version $Id: InsertScopeCopyCommand.java,v 1.3 2009/11/04 17:18:52 chsaad
 *          Exp $
 * @author Markus Bauer (markusbauer@users.sourceforge.net), Programming
 *         distributed Systems Lab, University of Augsburg, Germany,
 *         www.ds-lab.org
 */
public class InsertScopeCopyCommand extends Command
{

	/**
	 * The workflow editor.
	 */
	private WEEditor weeditor;

	/**
	 * The EMF EditingDomain.
	 */
	private EditingDomain editingDomain;

	/**
	 * A request to create the new scope.
	 */
	private CreateRequest request;

	/**
	 * The activity model.
	 */
	private Scope newScope;

	/**
	 * The point where to insert the new {@link Activity}.
	 */
	private Point insertPoint;

	/**
	 * EMF command to add the new elements to the scope.
	 */
	private CompoundCommand addCommand;

	/**
	 * The copied elements.
	 */
	private Collection elementsCopied;

	/**
	 * The top left point of the selected elements.
	 */
	private Point minPoint;

	/**
	 * The bottom right point of the selected elements.
	 */
	private Point maxPoint;


	/**
	 * @param editingDomain
	 *            The EMF EditingDomain.
	 * @param request
	 *            A request to create the new scope.
	 * @param scope
	 *            The scope model.
	 * @param insertPoint
	 *            The point where to insert the new {@link Scope}.
	 */
	public InsertScopeCopyCommand(WEEditor weeditor, EditingDomain editingDomain,
			CreateRequest request, Scope scope, Point insertPoint)
	{
		this.editingDomain = editingDomain;
		this.newScope = scope;
		this.insertPoint = insertPoint;
		this.request = request;
		this.weeditor = weeditor;
	}


	/**
	 * @return Returns the minPoint.
	 */
	public Point getMinPoint()
	{
		return minPoint;
	}


	/**
	 * @return Returns the maxPoint.
	 */
	public Point getMaxPoint()
	{
		return maxPoint;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#canExecute()
	 */
	@Override
	public boolean canExecute()
	{
		return editingDomain != null
				&& newScope != null
				&& (request != null && request.getNewObjectType() instanceof Class && Scope.class
						.isAssignableFrom(((Class) request.getNewObjectType())));
	}


	/**
	 * Prepares the copy, depending on the current state of the scope.
	 */
	public void prepareCopy()
	{
		// get old elements
		Scope oldScope = (Scope) request.getNewObject();
		EList oldReferencesInScope = EMFHelper.getReferencesForScope(weeditor, oldScope);
		EList oldReferenceEdgesInScope = EMFHelper.getReferenceEdgesForScope(weeditor,
				oldScope);

		// collect eeeeeverything to be copied
		Collection elementsToCopy = new ArrayList();
		elementsToCopy.addAll(oldScope.getNodes()); // add nodes
		elementsToCopy.addAll(oldScope.getEdges()); // add activityedges
		elementsToCopy.addAll(oldScope.getOwnedComment()); // add activityedges
		elementsToCopy.addAll(oldReferencesInScope); // add references
		elementsToCopy.addAll(oldReferenceEdgesInScope); // add referenceedges
		for (Object object : elementsToCopy.toArray()) // add layoutdata
		{
			if (object instanceof GraphicalElement)
			{
				elementsToCopy.addAll(LayoutDataManager.getLayoutDataForGraphicalElement(
						weeditor, (GraphicalElement) object));
			}
		}

		// we need to make our own copy since EcoreCopyFactory does not copy
		// activities
		elementsCopied = EcoreUtil.copyAll(elementsToCopy);

		// find the location of the top-left element
		minPoint = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE);
		maxPoint = new Point(0, 0);
		for (Object element : elementsToCopy)
		{
			// ignore elements with x=0 (probably unset coordinates)
			if (element instanceof GraphicalElement
					&& LayoutDataManager.getX(weeditor, (GraphicalElement) element) != 0)
			{
				minPoint = Point.min(minPoint, new Point(LayoutDataManager.getX(weeditor,
						(GraphicalElement) element), LayoutDataManager.getY(weeditor,
						(GraphicalElement) element)));

				maxPoint = Point.max(maxPoint, new Point(LayoutDataManager.getX(weeditor,
						(GraphicalElement) element)
						+ LayoutDataManager
								.getWidth(weeditor, (GraphicalElement) element),
						LayoutDataManager.getY(weeditor, (GraphicalElement) element)
								+ LayoutDataManager.getHeight(weeditor,
										(GraphicalElement) element)));
			}
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#execute()
	 */
	@Override
	public void execute()
	{
		// get new elements
		Collection newReferences = new ArrayList();
		Collection newReferenceEdges = new ArrayList();
		Collection newLayoutDatas = new ArrayList();
		Collection newActivityNodes = new ArrayList();
		Collection newActivityEdges = new ArrayList();
		Collection newComments = new ArrayList();

		for (Object object : elementsCopied)
		{
			if (object instanceof Reference)
			{
				newReferences.add(object);
				((Reference) object).setContainedIn(null);
			}
			else if (object instanceof ReferenceEdge)
			{
				newReferenceEdges.add(object);
				((ReferenceEdge) object).setContainedIn(null);
			}
			else if (object instanceof LayoutData)
			{
				newLayoutDatas.add(object);
			}
			else if (object instanceof ActivityNode)
			{
				newActivityNodes.add(object);
			}
			else if (object instanceof ActivityEdge)
			{
				newActivityEdges.add(object);
			}
			else if (object instanceof Comment)
			{
				newComments.add(object);
			}
		}

		// create the command
		addCommand = new CompoundCommand();

		// set the "contained in" in all references and referenceedges
		for (Object x : newReferences)
		{
			addCommand.appendAndExecute(SetCommand.create(editingDomain, x,
					ViewPackage.Literals.REFERENCE__CONTAINED_IN, newScope));
		}
		for (Object x : newReferenceEdges)
		{
			addCommand.appendAndExecute(SetCommand.create(editingDomain, x,
					ViewPackage.Literals.REFERENCE_EDGE__CONTAINED_IN, newScope));
		}

		// add layout data, references, referenceedges to diagram
		addCommand.appendAndExecute(AddCommand.create(editingDomain, EMFHelper
				.getDiagram(weeditor), ViewPackage.Literals.DIAGRAM__LAYOUT_DATA,
				newLayoutDatas));
		addCommand.appendAndExecute(AddCommand.create(editingDomain, EMFHelper
				.getDiagram(weeditor), ViewPackage.Literals.DIAGRAM__REFERENCES,
				newReferences));
		addCommand.appendAndExecute(AddCommand.create(editingDomain, EMFHelper
				.getDiagram(weeditor), ViewPackage.Literals.DIAGRAM__REFERENCE_EDGES,
				newReferenceEdges));

		// add activitynodes and activityedges to scope
		addCommand.appendAndExecute(AddCommand.create(editingDomain, newScope,
				ProcessesPackage.Literals.SCOPE__NODES, newActivityNodes));
		addCommand.appendAndExecute(AddCommand.create(editingDomain, newScope,
				ProcessesPackage.Literals.SCOPE__EDGES, newActivityEdges));

		// relocate to the insert point, but place the top-left element there
		// and don't insert the space above and left of the top-left element.
		for (Object element : elementsCopied)
		{
			if (element instanceof GraphicalElement)
			{
				LayoutDataManager.setX(weeditor, (GraphicalElement) element,
						LayoutDataManager.getX(weeditor, (GraphicalElement) element)
								+ insertPoint.x - minPoint.x);
				LayoutDataManager.setY(weeditor, (GraphicalElement) element,
						LayoutDataManager.getY(weeditor, (GraphicalElement) element)
								+ insertPoint.y - minPoint.y);
			}
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#redo()
	 */
	@Override
	public void redo()
	{
		addCommand.redo();
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#undo()
	 */
	@Override
	public void undo()
	{
		addCommand.undo();
	}

}
