/**
 * File:    ReconnectReferenceEdgeCommand.java
 * Created: 01.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
 *    Christian Saad, Programming distributed Systems Lab, University of Augsburg 
 *    	- rewrote from scratch
 *******************************************************************************/

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

import java.util.Collections;
import java.util.Iterator;

import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.edit.command.DeleteCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.jwt.meta.model.data.Data;
import org.eclipse.jwt.meta.model.processes.Action;
import org.eclipse.jwt.we.editors.WEEditor;
import org.eclipse.jwt.we.model.view.Reference;
import org.eclipse.jwt.we.model.view.ReferenceEdge;

/**
 * This command reconnects the Reference or Action part of a ReferenceEdge by
 * creating and executing the corresponding CreateReferenceEdge and
 * RemoveReferenceEdge Commands which in turn will modify the Feature
 * (PerformedBy, ExecutedBy) of the Action and the ReferenceEdge itself.
 * 
 * Since this is a very complex process, it is limited to References of the
 * types Role and Application. A reconnection is also only possible if the
 * corresponding feature slot of the Action is still free.
 * 
 * @version $Id: ReconnectReferenceEdgeCommand.java,v 1.2 2008/01/31 09:51:26
 *          flautenba Exp $
 * @author Christian Saad (csaad@gmx.net), Programming distributed Systems Lab,
 *         University of Augsburg, Germany, www.ds-lab.org
 */
public class ReconnectReferenceEdgeCommand extends org.eclipse.gef.commands.Command
{

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

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

	/**
	 * The new Reference.
	 */
	private Reference newReference;

	/**
	 * The new Action.
	 */
	private Action newAction;

	/**
	 * Command to remove the old ReferenceEdge which is to be reconnected.
	 */
	private Command removeOldRefEdgeCommand;

	/**
	 * Command to create the new ReferenceEdge.
	 */
	private CreateReferenceEdgeCommand createNewRefEdgeCommand;

	/**
	 * The old ReferenceEdge.
	 */
	private ReferenceEdge oldEdge;

	/**
	 * Indicates, whether the Reference has changed.
	 */
	private boolean referenceChanged = false;

	/**
	 * Indicates, whether the Action has changed.
	 */
	private boolean actionChanged = false;


	/**
	 * @param editingDomain
	 *            The EMF EditingDomain.
	 * @param edge
	 *            The edge to reconnect.
	 */
	public ReconnectReferenceEdgeCommand(WEEditor weeditor, EditingDomain editingDomain,
			ReferenceEdge oldEdge)
	{
		super();
		this.editingDomain = editingDomain;
		this.oldEdge = oldEdge;
		this.weeditor = weeditor;
	}


	/**
	 * Sets the new Reference.
	 * 
	 * @param reference
	 */
	public void setNewReference(Reference reference)
	{
		this.newReference = reference;

		if (reference != null && reference != getOldReference())
		{
			referenceChanged = true;
		}
	}


	/**
	 * Sets the new Action.
	 * 
	 * @param action
	 */
	public void setNewAction(Action action)
	{
		this.newAction = action;

		if (action != null && action != getOldAction())
		{
			actionChanged = true;
		}
	}


	/**
	 * Returns the old Reference.
	 * 
	 * @return The old reference.
	 */
	private Reference getOldReference()
	{
		return oldEdge.getReference();
	}


	/**
	 * Returns the old Action.
	 * 
	 * @return The old action.
	 */
	private Action getOldAction()
	{
		return oldEdge.getAction();
	}


	/**
	 * Returns the new Reference (if set).
	 * 
	 * @return The new reference.
	 */
	private Reference getNewReference()
	{
		return newReference;
	}


	/**
	 * Returns the new Action (if set).
	 * 
	 * @return The new action.
	 */
	private Action getNewAction()
	{
		return newAction;
	}


	/**
	 * Checks if the re-connection request is valid.
	 * 
	 * @return If the connection is valid.
	 */
	public boolean checkConnection()
	{
		// if nothing has changed, there is no need for reconnect
		if (!referenceChanged && !actionChanged)
		{
			return false;
		}

		// ??? (both changed - should not happen!)
		if (referenceChanged && actionChanged)
		{
			return false;
		}

		// don't allow reconnect for Data, because this would be too complex
		if (getOldReference().getReference() instanceof Data
				|| (getNewReference() != null && getNewReference().getReference() instanceof Data))
		{
			return false;
		}

		// if an refEdge to the SAME reference already exists -> abort
		Action action = actionChanged ? getNewAction() : getOldAction();
		Reference reference = referenceChanged ? getNewReference() : getOldReference();

		for (Iterator iterator = reference.getReferenceEdges().iterator(); iterator
				.hasNext();)
		{
			ReferenceEdge refEdge = (ReferenceEdge) iterator.next();

			if (refEdge.getAction().equals(action)
					&& refEdge.getReference().equals(reference))
			{
				return false;
			}
		}

		return true;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#canExecute()
	 */
	@Override
	public boolean canExecute()
	{
		if (editingDomain == null)
		{
			return false;
		}

		return checkConnection();
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#execute()
	 */
	@Override
	public void execute()
	{
		removeOldRefEdgeCommand = null;
		createNewRefEdgeCommand = null;

		// command: remove old refEdge from (old) action
		removeOldRefEdgeCommand = DeleteCommand.create(editingDomain, Collections
				.singleton(oldEdge));

		// if the action has changed: create refEdge for new action, old
		// reference
		// if the reference has changed: create refEdge for old action, new
		// reference
		Action action = actionChanged ? getNewAction() : getOldAction();
		Reference reference = referenceChanged ? getNewReference() : getOldReference();

		// create new refedge command
		createNewRefEdgeCommand = new CreateReferenceEdgeCommand(weeditor, editingDomain);
		createNewRefEdgeCommand.setSource(action);
		createNewRefEdgeCommand.setTarget(reference);
		createNewRefEdgeCommand.setDirection(0);

		// execute the commands
		if (removeOldRefEdgeCommand.canExecute() && createNewRefEdgeCommand.canExecute())
		{
			removeOldRefEdgeCommand.execute();
			createNewRefEdgeCommand.execute();
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#undo()
	 */
	@Override
	public void undo()
	{
		// undo the new refEdge and restore the old refEdge
		createNewRefEdgeCommand.undo();
		removeOldRefEdgeCommand.undo();
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#redo()
	 */
	@Override
	public void redo()
	{
		// remove the old refEdge and create the new refEdge
		removeOldRefEdgeCommand.redo();
		createNewRefEdgeCommand.redo();
	}


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

		if (removeOldRefEdgeCommand != null)
		{
			removeOldRefEdgeCommand.dispose();
		}

		if (createNewRefEdgeCommand != null)
		{
			createNewRefEdgeCommand.dispose();
		}
	}

}