/*******************************************************************************
 * Copyright (c) 2005 IBM Corporation and others.
 * 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.bpel.ui.commands;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import org.eclipse.bpel.model.Activity;
import org.eclipse.bpel.model.Flow;
import org.eclipse.bpel.model.Link;
import org.eclipse.bpel.model.Source;
import org.eclipse.bpel.model.Sources;
import org.eclipse.bpel.model.Target;
import org.eclipse.bpel.model.Targets;
import org.eclipse.bpel.ui.commands.util.AutoUndoCommand;
import org.eclipse.bpel.ui.util.FlowLinkUtil;
import org.eclipse.bpel.ui.util.ModelHelper;



/**
 * Revalidate all the links for which the given activity(s) are the source or target.
 * 
 * For each link, the link should be contained in the innermost enclosing Flow which
 * contains both the source and the target.  If there is no such flow, the link is
 * deleted.  If there is such a flow and the Link is not currently owned by it, the
 * Link is moved into that Flow (and renamed, if necessary, to prevent naming collisions). 
 */
public class RevalidateLinksCommand extends AutoUndoCommand {

	List<Activity> activities;
	CompoundCommand deleteLinksCmd;
	
	public RevalidateLinksCommand(Collection activityCollection) {
		super(new ArrayList(1));
		this.activities = new ArrayList(activityCollection.size());
		this.activities.addAll(activityCollection);
		if (!activityCollection.isEmpty()) {
			addModelRoot(ModelHelper.getProcess(activityCollection.iterator().next()));
		}
	}
	
	// TODO: can we use the unique name support instead?
	protected boolean isAcceptableLinkName(Flow flow, String proposedName) {
		for (Iterator it = FlowLinkUtil.getFlowLinks(flow).iterator(); it.hasNext(); ) {
			Link link = (Link)it.next();
			if (proposedName.equals(link.getName())) return false;
		}
		return true;
	}
	
	protected void revalidateLink(Link link) {
		Flow linkFlow = (Flow)(link.eContainer().eContainer());
		Activity source = FlowLinkUtil.getLinkSource(link);
		Activity target = FlowLinkUtil.getLinkTarget(link);
		Flow commonFlow = FlowLinkUtil.getCommonFlow(source, target);
		if (commonFlow == null) {
			// We must delete this link.  Queue it up for deletion (don't delete
			// right away, as that would disturb the list(s) of sources/targets
			// which we are iterating over).
			deleteLinksCmd.add(new DeleteLinkCommand(link));
		} else if (commonFlow != linkFlow) {
			// Move this link into the commonFlow.
			// First, make sure none of the links in the commonFlow has the same name!
			if (!isAcceptableLinkName(commonFlow, link.getName())) {
				String s;
				int i = 0;
				do {
					++i; s = link.getName()+"_"+i; //$NON-NLS-1$
				} while (!isAcceptableLinkName(commonFlow, s));
				link.setName(s);
			}
			FlowLinkUtil.removeFlowLink(linkFlow, link);
			FlowLinkUtil.addFlowLink(commonFlow, link);
		}
	}
	
	@Override
	public void doExecute() {
		deleteLinksCmd = new CompoundCommand();
		
		// Figure out which model objects are being moved.
		HashSet revalidatingSet = new HashSet();
		for (Iterator<Activity> it = activities.iterator(); it.hasNext(); ) {
			Activity activity = it.next();
			ModelHelper.addSubtreeToCollection(activity, revalidatingSet);
		}
		for (Iterator it = revalidatingSet.iterator(); it.hasNext(); ) {
			Object object = it.next();
			if (object instanceof Activity) { 
				Activity activity = (Activity)object;
				Sources sources = activity.getSources();
				if (sources != null) {
					Iterator<Source> it2 = sources.getChildren().iterator();
					while (it2.hasNext()) revalidateLink((it2.next()).getLink());
				}
				Targets targets = activity.getTargets();
				if (targets != null) {
					Iterator<Target> it2 = targets.getChildren().iterator();
					while (it2.hasNext()) revalidateLink((it2.next()).getLink());
				}
			}
		}
		// delete any links that we queued up for deletion.
		if (!deleteLinksCmd.isEmpty()) deleteLinksCmd.execute();
	}
}
