/**
 * File:    InsertScopeCommand.java
 * Created: 15.05.2007
 *
 *
/*******************************************************************************
 * 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:
 *    Christian Saad, Programming distributed Systems Lab, University of Augsburg 
 *    	- initial API and implementation
 *******************************************************************************/

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

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jwt.meta.commands.interfaces.IInterruptibleCommand;
import org.eclipse.jwt.meta.model.processes.Activity;
import org.eclipse.jwt.meta.model.processes.ActivityLinkNode;
import org.eclipse.jwt.meta.model.processes.ProcessesPackage;
import org.eclipse.jwt.meta.model.processes.Scope;
import org.eclipse.jwt.we.PluginProperties;
import org.eclipse.jwt.we.commands.gefEmfAdapter.EmfToGefCommandAdapter;
import org.eclipse.jwt.we.editors.WEEditor;
import org.eclipse.jwt.we.editors.preferences.PreferenceReader;
import org.eclipse.jwt.we.misc.factories.EcoreFactory;
import org.eclipse.jwt.we.misc.util.GeneralHelper;
import org.eclipse.jwt.we.parts.processes.ScopeEditPart;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;

/**
 * This offers the user a choice how to insert a {@link Scope} into another
 * {@link Scope} and prepares and executes an insert command based on that
 * choice.
 * 
 * The three different possibilities are: - Insert a direct copy - Insert a copy
 * into an structured activity node - Link to a new activity link node
 * 
 * This is partly based on the former AgilProTransferDropTargetListener by Wolf
 * Fischer.
 * 
 * @version $Id: InsertScopeCommand.java,v 1.8 2009-12-10 12:51:16 chsaad Exp $
 * @author Christian Saad (csaad@gmx.net), Programming distributed Systems Lab,
 *         University of Augsburg, Germany, www.ds-lab.org
 */
public class InsertScopeCommand extends Command implements IInterruptibleCommand
{

	/**
	 * 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 mainScope;

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

	/**
	 * GEF command to add the new elements to the scope.
	 */
	private CompoundCommand insertCommand;

	/**
	 * The object which should be selected after inserting.
	 */
	private Object select;

	/**
	 * The result of the dialog on how the scope should be inserted.
	 */
	private int result;


	/**
	 * @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 InsertScopeCommand(WEEditor weeditor, EditingDomain editingDomain,
			CreateRequest request, Scope scope, Point insertPoint)
	{
		super(PluginProperties.insertActivity);

		this.editingDomain = editingDomain;
		this.mainScope = scope;
		this.request = request;
		this.weeditor = weeditor;
		this.insertPoint = insertPoint;

		if (insertPoint == null)
		{
			insertPoint = new Point(0, 0);
		}
	}


	/**
	 * Select an object
	 */
	private void selectAddedObject()
	{
		if (select == null)
		{
			return;
		}
		getViewer().getControl().forceFocus();
		Object editpart = getViewer().getEditPartRegistry().get(select);
		if (editpart instanceof EditPart)
		{
			// Force a layout first.
			getViewer().flush();
			getViewer().select((EditPart) editpart);
		}
	}


	/**
	 * Returns the editpartviewer
	 */
	private EditPartViewer getViewer()
	{
		return weeditor.getCurrentActivitySheet().getGraphicalViewer();
	}


	/**
	 * Shows a dialog which offers different possibilites how to insert an
	 * {@link Activity} into another {@link Activity} (gets called immediately
	 * before the command is executed).
	 * 
	 * @returns A value which determines how the scope should be inserted.
	 */
	public boolean checkIfCommandCanBeExecuted()
	{
		// prepare strings
		Shell shell = GeneralHelper.getActiveShell();
		String mainScopeName = org.eclipse.jwt.meta.PluginProperties.model_Unnamed_name;
		String insertScopeName = org.eclipse.jwt.meta.PluginProperties.model_Unnamed_name;
		try
		{
			mainScopeName = ((Activity) mainScope).getName() != null ? ((Activity) mainScope)
					.getName()
					: "<>"; //$NON-NLS-1$
			insertScopeName = ((Activity) request.getNewObject()).getName() != null ? ((Activity) request
					.getNewObject()).getName()
					: "<>"; //$NON-NLS-1$
		}
		catch (Exception ex)
		{
			ex.printStackTrace();
		}
		String title = PluginProperties.editor_DropActivity_Title;
		String question = NLS.bind(PluginProperties.editor_DropActivity_Question,
				mainScopeName, insertScopeName);
		String[] buttons = new String[]
		{ PluginProperties.editor_DropActivity_Copy,
				PluginProperties.editor_DropActivity_SAN,
				PluginProperties.editor_DropActivity_LAN,
				PluginProperties.editor_Cancel_message };

		// show dialog
		MessageDialog md = new MessageDialog(shell, title, null, question,
				MessageDialog.QUESTION, buttons, 0);

		md.setBlockOnOpen(true);
		result = md.open();

		// if user canceled insert operation (result=3) -> abort
		return result != 3;
	}


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


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#execute()
	 */
	@Override
	public void execute()
	{
		// if nothing else applies, select main scope
		this.select = mainScope;

		// prepare the corresponding insert command
		insertCommand = new CompoundCommand();

		switch (result)
		{
			/**
			 * Insert a copy of the elements: Standard
			 */
			case 0:
			{
				InsertScopeCopyCommand scopeCopy = new InsertScopeCopyCommand(weeditor,
						editingDomain, request, mainScope, insertPoint);
				scopeCopy.prepareCopy();
				insertCommand.add(scopeCopy);
			}
				break;

			/**
			 * This is the insert as a structuredactivitynode. First, a
			 * structuredactivitynode is created, then the new SAN is set as the
			 * targeteditpart and afterwards simply the elements get copied into
			 * the SAN. Compare this to first case...
			 */
			case 1:
			{
				try
				{
					// create a createrequest for structuredactivitynode
					CreateRequest cr = new CreateRequest();
					cr.setType(RequestConstants.REQ_CREATE);
					cr.setLocation(insertPoint);
					cr.setFactory(new EcoreFactory(
							ProcessesPackage.Literals.STRUCTURED_ACTIVITY_NODE));

					// prepare insertscopecopycommand
					InsertScopeCopyCommand scopeCopy = new InsertScopeCopyCommand(
							weeditor, editingDomain, request, (Scope) cr.getNewObject(),
							new Point(0, 0));
					scopeCopy.prepareCopy();
					cr.setSize(new Dimension(scopeCopy.getMaxPoint().x
							- scopeCopy.getMinPoint().x + 70, scopeCopy.getMaxPoint().y
							- scopeCopy.getMinPoint().y + 70));

					// add structuredactivitynode command
					insertCommand.add(((ScopeEditPart) getViewer().getEditPartRegistry()
							.get(mainScope)).getCommand(cr));

					// add scopycopy command
					insertCommand.add(scopeCopy);

					// select the structuredactivitynode
					this.select = cr.getNewObject();
				}
				catch (Exception ex)
				{
					ex.printStackTrace();
				}
			}
				break;

			/**
			 * The third case creates an ActivityLinkNode and afterwards get the
			 * dragged Activity and sets this in the linksto association of the
			 * ALN. Remarks: This one does not create a copy of the dragged ALN
			 * but works on the dragged version directly
			 */
			case 2:
			{
				// create a createrequest for activitylinknode
				CreateRequest cr = new CreateRequest();
				cr.setType(RequestConstants.REQ_CREATE);
				cr.setLocation(insertPoint);
				cr.setSize(PreferenceReader.appearanceFigureIconSize.get());
				cr.setFactory(new EcoreFactory(
						ProcessesPackage.Literals.ACTIVITY_LINK_NODE));

				try
				{
					// add activitylinknode command
					insertCommand.add(((ScopeEditPart) getViewer().getEditPartRegistry()
							.get(mainScope)).getCommand(cr));

					// add a set command which will set the link feature of the
					// link
					// node
					// EcoreCopyFactory will return original activity instead of
					// copy
					insertCommand.add((new EmfToGefCommandAdapter(SetCommand.create(
							editingDomain, cr.getNewObject(),
							ProcessesPackage.Literals.ACTIVITY_LINK_NODE__LINKSTO,
							request.getNewObject()))));

					// set the name of the link node to the name of the linked
					// activity
					((ActivityLinkNode) cr.getNewObject()).setName(((Activity) request
							.getNewObject()).getName());

					// select the activitylinknode
					this.select = cr.getNewObject();
				}
				catch (Exception ex)
				{
					ex.printStackTrace();
				}
			}
				break;

			/**
			 * If nothing else applies (e.g. canceled) -> break
			 */
			default:
				break;
		}

		if (insertCommand.getCommands().size() != 0 && insertCommand.canExecute())
		{
			// execute the command to insert the scope
			insertCommand.execute();

			// select the added object
			selectAddedObject();
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#redo()
	 */
	@Override
	public void redo()
	{
		if (insertCommand.getCommands().size() != 0 && insertCommand.canExecute())
		{
			insertCommand.redo();
		}

		// select the added object
		selectAddedObject();
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#undo()
	 */
	@Override
	public void undo()
	{
		if (insertCommand.getCommands().size() != 0 && insertCommand.canUndo())
		{
			insertCommand.undo();
		}
	}

}