/*
 * This file has been developed at the University of Munich, Chair for Programming & Software Engineering.
 * 
 * This file is licensed under the Eclipse Public License (EPL) 1.0
 * 
 */
package eu.mdd4soa.trans.uml2smm.ui.actions;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.uml2.uml.Activity;
import org.eclipse.uml2.uml.Behavior;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.Port;
import org.eclipse.uml2.uml.ProtocolStateMachine;
import org.eclipse.uml2.uml.Type;

import eu.mdd4soa.smm.exception.InvalidSetupException;
import eu.mdd4soa.smm.exception.TransformationException;
import eu.mdd4soa.smm.statik.Participant;
import eu.mdd4soa.trans.uml2smm.StereotypeUtil;
import eu.mdd4soa.trans.uml2smm.StereotypeUtil.StereoType;
import eu.mdd4soa.trans.uml2smm.UML2SMM2Plugin;
import eu.mdd4soa.trans.uml2smm.UMLEMFUtil;
import eu.mdd4soa.trans.uml2smm.transform.BehaviourConverter;
import eu.mdd4soa.trans.uml2smm.transform.ProtocolConverter;
import eu.mdd4soa.trans.uml2smm.transform.StaticConverter;
import eu.mdd4soa.trans.uml2smm.ui.wizard.TransformerWizard;

public class Convert2SMMAction implements IObjectActionDelegate {

	private IWorkbenchPart fWorkbenchPart;

	private IFile fSelectedFile;

	private List<Participant> fParticipants;

	@Override
	public void run(IAction action) {

		if (fSelectedFile == null) {
			MessageDialog.openError(getShell(), "No File", "Attempt to run conversion without a file -- exiting");
			return;
		}

		try {
			Model umlModel= UMLEMFUtil.loadUMLModelFromFile(fSelectedFile);

			Map<Class, List<Activity>> availableActivities= UMLEMFUtil.getParticipantsAndActivities(umlModel);

			TransformerWizard wizard= new TransformerWizard();
			wizard.init(availableActivities, fSelectedFile.getParent());

			WizardDialog wDialog= new WizardDialog(getShell(), wizard);
			wDialog.setBlockOnOpen(true);

			if (wDialog.open() == WizardDialog.OK) {

				Map<Class, List<Activity>> activitiesToUse= wizard.getSelectedActivities();
				IContainer outputDirectoryToUse= wizard.getOutputDirectory();
				boolean open= wizard.getOpen();

				fParticipants= new ArrayList<Participant>();

				try {

					for (Class participant : activitiesToUse.keySet()) {

						Participant ismParticipant= StaticConverter.convert(participant);
						String partName= ismParticipant.getName();

						for (Activity activity : activitiesToUse.get(participant))
							BehaviourConverter.convert(ismParticipant, activity);

						Map<Class, ProtocolStateMachine> stateMachines= getStateMachines(participant, ismParticipant);
						for (Class portType : stateMachines.keySet())
							ProtocolConverter.convert(ismParticipant, portType, stateMachines.get(portType));

						storeAndOpen(ismParticipant, outputDirectoryToUse, partName + ".xmi", open);
						fParticipants.add(ismParticipant);
					}

				} catch (TransformationException e) {
					MessageDialog.openError(getShell(), "UML2SMM", "Found a problem while transforming: " + e.getMessage());
					UML2SMM2Plugin.logError(e);
				}
			}

		} catch (InvalidSetupException e) {
			MessageDialog.openError(getShell(), "No Model", "Problem loading model: " + e.getMessage());
		}

	}

	private Map<Class, ProtocolStateMachine> getStateMachines(Class participant, Participant ismParticipant) throws TransformationException {

		Map<Class, ProtocolStateMachine> machines= new HashMap<Class, ProtocolStateMachine>();

		EList<Port> ownedPorts= participant.getOwnedPorts();
		for (Port port : ownedPorts) {

			if (!StereotypeUtil.hasStereotype(port, StereoType.SERVICEPOINT) && (!StereotypeUtil.hasStereotype(port, StereoType.REQUESTPOINT)))
				throw new TransformationException("Participant " + participant.getQualifiedName()
						+ " has a port which is not a servicepoint or requestpoint: " + port.getQualifiedName());

			Type type= port.getType();
			if (type instanceof Class && (StereotypeUtil.hasStereotype(type, StereoType.SERVICEINTERFACE))) {

				EList<Behavior> ownedBehaviors2= ((Class) type).getOwnedBehaviors();
				for (Behavior protocol : ownedBehaviors2) {
					if (protocol instanceof ProtocolStateMachine) {
						machines.put((Class) type, (ProtocolStateMachine) protocol);
					}
				}
			}
		}

		return machines;
	}

	public List<Participant> getParticipants() {
		return fParticipants;
	}

	private void storeAndOpen(Participant ismParticipant, IContainer outputDirectoryToUse, String fileName, boolean open)
			throws InvalidSetupException {
		IFile file= outputDirectoryToUse.getFile(new Path(fileName));

		Participant copy= (Participant) EcoreUtil.copy(ismParticipant);
		UMLEMFUtil.storeSMM(file, copy);
		try {
			file.refreshLocal(0, new NullProgressMonitor());
		} catch (CoreException e) {
			UML2SMM2Plugin.logError(e);
		}

		if (open) {
			// TODO open.
		}
	}


	@Override
	public void setActivePart(IAction action, IWorkbenchPart targetPart) {
		fWorkbenchPart= targetPart;
	}

	@Override
	public void selectionChanged(IAction action, ISelection selection) {
		if (selection == null || ! (selection instanceof StructuredSelection) && ( ((StructuredSelection) selection).isEmpty()))
			fSelectedFile= null;
		else {
			Object element= ((StructuredSelection) selection).getFirstElement();
			if (element instanceof IFile)
				fSelectedFile= (IFile) element;
			else
				fSelectedFile= null;
		}
	}

	private Shell getShell() {
		return fWorkbenchPart.getSite().getShell();
	}

}
