/**
 * File:    WEPasteFromClipboardCommand.java
 * Created: 14.06.2008
 *
 *
/*******************************************************************************
 * 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 
 *    	- rewrote from scratch
 *******************************************************************************/

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

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map.Entry;

import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.StrictCompoundCommand;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.PasteFromClipboardCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.jwt.meta.model.core.GraphicalElement;
import org.eclipse.jwt.meta.model.core.ReferenceableElement;
import org.eclipse.jwt.meta.model.processes.Activity;
import org.eclipse.jwt.meta.model.processes.ActivityLinkNode;
import org.eclipse.jwt.meta.model.processes.Scope;
import org.eclipse.jwt.meta.model.processes.StructuredActivityNode;
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;

/**
 * This is a custom PasteFromClipboard command that slightly changes the
 * position of the GraphicalElements in the clipboard each time they are pasted
 * into the model, so that the user can distinguish them from the existing
 * elements.
 * 
 * @version $Id: CustomPasteFromClipboardCommand.java,v 1.2 2008/09/30 07:53:40
 *          flautenba Exp $
 * @author Christian Saad (csaad@gmx.net), Programming distributed Systems Lab,
 *         University of Augsburg, Germany, www.ds-lab.org
 */
public class WEPasteFromClipboardCommand extends PasteFromClipboardCommand
{

	private Collection clipboardComplete;
	private Collection clipboardWithoutDiagramData;
	private Collection clipboardLayoutData;
	private Collection clipboardReferences;
	private Collection clipboardReferenceEdges;

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


	/**
	 * The constructor.
	 * 
	 * @param domain
	 * @param owner
	 * @param feature
	 * @param index
	 * @param optimize
	 */
	public WEPasteFromClipboardCommand(WEEditor weeditor, EditingDomain domain,
			Object owner, Object feature, int index, boolean optimize)
	{
		super(domain, owner, feature, index, optimize);

		this.weeditor = weeditor;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.command.PasteFromClipboardCommand#prepare()
	 */
	@Override
	protected boolean prepare()
	{
		clipboardComplete = new ArrayList();
		clipboardWithoutDiagramData = new ArrayList();
		clipboardLayoutData = new ArrayList();
		clipboardReferences = new ArrayList();
		clipboardReferenceEdges = new ArrayList();

		// split diagram data and other items in the clipboard
		if (domain.getClipboard() != null)
		{
			for (Object object : domain.getClipboard())
			{
				if (object instanceof LayoutData)
				{
					clipboardLayoutData.add(object);
				}
				else if (object instanceof Reference)
				{
					clipboardReferences.add(object);
				}
				else if (object instanceof ReferenceEdge)
				{
					clipboardReferenceEdges.add(object);
				}
				else
				{
					clipboardWithoutDiagramData.add(object);
				}
			}

			// set clipboard to the elements which are not from diagram
			clipboardComplete.addAll(domain.getClipboard());
			domain.setClipboard(new ArrayList(clipboardWithoutDiagramData));
		}

		// prepare command for non-diagram items
		boolean result = true;
		if (clipboardWithoutDiagramData.size() > 0)
		{
			// call original prepare
			result = super.prepare();
		}
		else
		{
			// omit command because there are no non-diagram elements to paste
			command = null;
		}

		// prepend add commands for diagram datas
		StrictCompoundCommand newCommand = new StrictCompoundCommandPessimistic();

		if (clipboardLayoutData.size() > 0)
		{
			Command addCommandLayoutData = AddCommand.create(domain,
					EMFHelper.getDiagram(weeditor), ViewPackage.DIAGRAM__LAYOUT_DATA,
					clipboardLayoutData);
			newCommand.append(addCommandLayoutData);
		}
		if (clipboardReferences.size() > 0)
		{
			Command addCommandReferences = AddCommand.create(domain,
					EMFHelper.getDiagram(weeditor), ViewPackage.DIAGRAM__REFERENCES,
					clipboardReferences);
			newCommand.append(addCommandReferences);
		}
		if (clipboardReferenceEdges.size() > 0)
		{
			Command addCommandReferenceEdges = AddCommand.create(domain,
					EMFHelper.getDiagram(weeditor), ViewPackage.DIAGRAM__REFERENCE_EDGES,
					clipboardReferenceEdges);
			newCommand.append(addCommandReferenceEdges);
		}
		if (command != null)
		{
			newCommand.append(command);
		}

		command = newCommand;

		// restore old clipboard state with diagram data
		domain.setClipboard(new ArrayList(clipboardComplete));

		return result && clipboardComplete.size() > 0;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.command.PasteFromClipboardCommand#doRedo()
	 */
	@Override
	public void doRedo()
	{
		super.doRedo();

		// recreate the copy in the clipboard
		WECopyToClipboardCommand copyCommand = new WECopyToClipboardCommand(weeditor,
				domain, clipboardComplete);
		if (copyCommand.canExecute())
		{
			copyCommand.execute();
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.command.PasteFromClipboardCommand#doUndo()
	 */
	@Override
	public void doUndo()
	{
		super.doUndo();

		// set again the original clipboard content (which has been removed from
		// the
		// model)
		domain.setClipboard(new ArrayList(clipboardComplete));
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.command.PasteFromClipboardCommand#doExecute()
	 */
	@Override
	public void doExecute()
	{
		if (domain.getClipboard() != null)
		{
			// store current state of the clipboard
			clipboardComplete.clear();
			clipboardComplete.addAll(domain.getClipboard());

			// set clipboard to the elements which are not from diagram
			domain.setClipboard(new ArrayList(clipboardWithoutDiagramData));
		}

		// paste clipboard objects into model
		super.doExecute();

		// create again a copy of the elements and store it in the clipboard
		// (for the next paste)
		WECopyToClipboardCommand copyCommand = new WECopyToClipboardCommand(weeditor,
				domain, clipboardComplete);
		if (copyCommand.canExecute())
		{
			copyCommand.execute();
		}

		// unset properties which reference elements not in the clipboard
		for (Entry<EObject, Collection<Setting>> entry : EcoreUtil.ExternalCrossReferencer
				.find(clipboardComplete).entrySet())
		{
			for (Setting setting : entry.getValue())
			{
				if ((setting.getEObject() instanceof Reference && entry.getKey() instanceof ReferenceableElement)
						|| (setting.getEObject() instanceof ActivityLinkNode && entry
								.getKey() instanceof Activity))
				{
					// references are allowed to reference referenceableelements
					continue;
				}
				else if (setting.getEStructuralFeature().isMany())
				{
					((Collection) setting.getEObject().eGet(
							setting.getEStructuralFeature())).remove(entry.getKey());
				}
				else
				{
					setting.getEObject().eSet(setting.getEStructuralFeature(), null);
				}
			}
		}

		// set scope for view elements
		for (Iterator iterator = clipboardComplete.iterator(); iterator.hasNext();)
		{
			EObject object = (EObject) iterator.next();

			if (object instanceof Reference)
			{
				((Reference) object).setContainedIn((Scope) owner);
			}
			else if (object instanceof ReferenceEdge)
			{
				((ReferenceEdge) object).setContainedIn((Scope) owner);
			}
		}

		// add/remove x/y-offset of objects to be pasted
		if (owner instanceof StructuredActivityNode)
		{
			// paste into san: remove offset
			int x = 32000, y = 32000;

			// get the offset of the element closest to the origin
			for (Iterator iterator = clipboardComplete.iterator(); iterator.hasNext();)
			{
				EObject object = (EObject) iterator.next();

				if (object instanceof GraphicalElement)
				{
					GraphicalElement graphicalElement = (GraphicalElement) object;

					x = Math.min(x, LayoutDataManager.getX(weeditor, graphicalElement));
					y = Math.min(y, LayoutDataManager.getY(weeditor, graphicalElement));
				}
			}

			// give it 15 points tolerance
			x -= 15;
			y -= 15;

			// set offset of objects
			for (Iterator iterator = clipboardComplete.iterator(); iterator.hasNext();)
			{
				EObject object = (EObject) iterator.next();

				// if it is a graphical element -> adjust the position
				if (object instanceof GraphicalElement)
				{
					GraphicalElement graphicalElement = (GraphicalElement) object;

					LayoutDataManager.setX(weeditor, graphicalElement,
							LayoutDataManager.getX(weeditor, graphicalElement) - x);
					LayoutDataManager.setY(weeditor, graphicalElement,
							LayoutDataManager.getY(weeditor, graphicalElement) - y);
				}
			}
		}
		else
		{
			// paste anywhere else: add offset
			for (Iterator iterator = clipboardComplete.iterator(); iterator.hasNext();)
			{
				EObject object = (EObject) iterator.next();

				// if it is a graphical element -> adjust the position
				if (object instanceof GraphicalElement)
				{
					GraphicalElement graphicalElement = (GraphicalElement) object;

					LayoutDataManager.setX(weeditor, graphicalElement,
							LayoutDataManager.getX(weeditor, graphicalElement) + 15);
					LayoutDataManager.setY(weeditor, graphicalElement,
							LayoutDataManager.getY(weeditor, graphicalElement) + 15);
				}
			}
		}

		// refresh page (if c&p from activity1 to activity2, inserting new view
		// elements doesn't trigger update)
		if (weeditor.getCurrentActivitySheet() != null)
		{
			weeditor.getCurrentActivitySheet().refreshEditorSheet();
		}

		/*
		 * // remove layoutdata (except from current view) for (Object object :
		 * getResult()) { LayoutDataManager.removeLayoutData((EObject) object,
		 * Views.getInstance() .getSelectedView().getInternalName()); }
		 */
	}


	/**
	 * Returns the elements which should be selected after the paste.
	 * 
	 * @return The elements to be selected.
	 */
	public Collection elementsToSelect()
	{
		Collection elementsToSelect = new ArrayList();

		elementsToSelect.addAll(command.getResult());
		elementsToSelect.addAll(clipboardReferences);

		return elementsToSelect;
	}

}
