/*
 * 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.uml4soa.utbm.ui.eclipse.actions;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.miowb.model.mio.ModalIOAutomaton;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.uml2.uml.Activity;
import org.eclipse.uml2.uml.Behavior;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.ProtocolStateMachine;

import eu.uml4soa.utbm.u2m.SemanticsException;
import eu.uml4soa.utbm.u2m.U2MPlugin;
import eu.uml4soa.utbm.ui.eclipse.runner.ParticipantSemantics;
import eu.uml4soa.utbm.ui.eclipse.runner.SemanticsRunner;

public class SemanticsAction implements IObjectActionDelegate {

	private static final String EDITORID= "net.miowb.workbench.ui.editor.ModalIOEditor";

	private IWorkbenchPartSite fSite;

	private IFile fSelectedFile;

	private List<IFile> fOutput;

	public SemanticsAction() {
		super();
	}

	public void setActivePart(IAction action, IWorkbenchPart targetPart) {
		fSite= targetPart.getSite();
	}

	public void run(IAction action) {

		final SemanticsRunner runner= new SemanticsRunner(fSelectedFile);

		IRunnableWithProgress setupRunnable= new IRunnableWithProgress() {

			@Override
			public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
				try {
					runner.loadUML2Model(monitor);
				} catch (SemanticsException e) {
					throw new InvocationTargetException(e);
				}
			}
		};

		IRunnableWithProgress workingRunnable= new IRunnableWithProgress() {

			@Override
			public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
				try {
					runner.execute(monitor);
				} catch (SemanticsException e) {
					throw new InvocationTargetException(e);
				}
			}
		};

		try {

			runWithProgressDialog(setupRunnable);

			// Show dialog
			Map<Class, List<Behavior>> availableBehaviours= runner.getAvailableBehaviours();

			if (availableBehaviours.isEmpty()) {
				MessageDialog.openError(fSite.getShell(), "Problem", "There are no participants in the given UML model.");
			}

			// Try to find a nice output directory.
			IContainer outputDirectory= fSelectedFile.getParent();
			IContainer parentParent= outputDirectory.getParent();
			IFolder folder= parentParent.getFolder(new Path("mio"));
			if (folder.exists())
				outputDirectory= (IContainer) folder;


			SemanticsWizard wizard= new SemanticsWizard();
			wizard.init(availableBehaviours, outputDirectory, "UML to MIO Mapping");


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

			if (wDialog.open() == WizardDialog.OK) {
				runner.setSelectedBehaviors(wizard.getSelectedBehaviours());

				runWithProgressDialog(workingRunnable);

				Map<Class, ParticipantSemantics> semanticsMapping= runner.getSemanticsMapping();
				fOutput= output(semanticsMapping, wizard.getOutputDirectory());

				if (wizard.getOpen()) {
					for (IFile iFile : fOutput) {
						fSite.getPage().openEditor(new FileEditorInput(iFile), EDITORID);
					}
				}
			}

		} catch (SemanticsException e) {
			MessageDialog.openError(fSite.getShell(), "Error during the run", e.getMessage());
			U2MPlugin.logError(e);
			return;
		} catch (Throwable e) {
			U2MPlugin.logError(e);
			return;
		}

	}

	public List<IFile> getOutput() {
		return fOutput;
	}

	public void selectionChanged(IAction action, ISelection selection) {
		if (selection instanceof IStructuredSelection) {
			Object element= ((IStructuredSelection) selection).getFirstElement();
			if (element instanceof IFile) {
				fSelectedFile= (IFile) element;
			}
		} else
			fSelectedFile= null;
	}

	private void runWithProgressDialog(IRunnableWithProgress prog) throws InterruptedException, SemanticsException, Throwable,
			InvocationTargetException {
		try {
			ProgressMonitorDialog dialog= new ProgressMonitorDialog(fSite.getShell());
			dialog.run(true, true, prog);
		} catch (InvocationTargetException e) {
			Throwable cause= e.getCause();
			if (cause != null) {
				if (cause instanceof SemanticsException)
					throw (SemanticsException) cause;
				else
					throw cause;
			} else
				throw e;
		}
	}

	private List<IFile> output(Map<Class, ParticipantSemantics> semanticsMapping, IContainer outputDirectory) throws SemanticsException {

		List<IFile> files= new ArrayList<IFile>();

		for (Class c : semanticsMapping.keySet()) {

			ParticipantSemantics participantSemantics= semanticsMapping.get(c);

			Map<Behavior, ModalIOAutomaton> sems= participantSemantics.getAvailableSemantics();
			for (Behavior b : sems.keySet()) {

				ModalIOAutomaton aut= sems.get(b);

				StringBuilder name= new StringBuilder();
				name.append(c.getName());
				name.append("_");
				if (b instanceof Activity) {
					name.append("Orchestration");
				} else {
					name.append("Protocol_");
					ProtocolStateMachine machine= (ProtocolStateMachine) b;
					name.append(machine.getName());
				}
				name.append(".mio");

				IFile file= outputDirectory.getFile(new Path(name.toString()));
				ResourceSet rSet= new ResourceSetImpl();

				Resource resource= rSet.createResource(convertToURI(file));

				resource.getContents().add(aut);
				try {
					HashMap<String, String> options= new HashMap<String, String>();
					options.put(XMIResource.OPTION_ENCODING, "UTF-8");

					resource.save(null);

					files.add(file);

				} catch (IOException e) {
					throw new SemanticsException("Unable to write output XMI file.", e);
				}
			}
		}

		try {
			outputDirectory.refreshLocal(IFile.DEPTH_INFINITE, new NullProgressMonitor());
		} catch (CoreException e) {
			throw new SemanticsException("Problem refreshing the output directory.", e);
		}

		return files;

	}

	protected URI convertToURI(Object file) {
		return URI.createFileURI( ((IFile) file).getLocation().toString());
	}

}
