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

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.eclipse.core.resources.IFile;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.Factory.Registry;
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.emf.ecore.xmi.impl.EcoreResourceFactoryImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.eclipse.emf.ecore.xmi.impl.XMLContentHandlerImpl;
import org.eclipse.emf.ecore.xmi.impl.XMLResourceFactoryImpl;
import org.eclipse.uml2.uml.Activity;
import org.eclipse.uml2.uml.Behavior;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Component;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.resource.UMLResource;

import eu.mdd4soa.smm.exception.InvalidSetupException;
import eu.mdd4soa.trans.uml2smm.StereotypeUtil.StereoType;

public class UMLEMFUtil {

	public static Model loadUMLModelFromFile(IFile file) throws InvalidSetupException {

		if (file == null)
			throw new InvalidSetupException("File is null.");

		if (!file.exists())
			throw new InvalidSetupException("File does not exist: " + file.getFullPath().toOSString());

		return loadUMLModelFromURI(convertToURI(file));
	}

	public static Model loadUMLModelFromFile(File file) throws InvalidSetupException {

		if (file == null)
			throw new InvalidSetupException("File is null.");

		if (!file.exists())
			throw new InvalidSetupException("File does not exist: " + file.getAbsolutePath());

		return loadUMLModelFromURI(convertToURI(file));
	}

	private static Model loadUMLModelFromURI(URI fileURI) throws InvalidSetupException {

		ResourceSet resourceSet= new ResourceSetImpl();

		addHeadlessInformation(resourceSet);

		Resource resource= resourceSet.getResource(fileURI, true);

		EList<EObject> contents= resource.getContents();
		if (contents == null || contents.size() == 0)
			throw new InvalidSetupException("Could not read EMF model from input file.");

		EObject object= resource.getContents().get(0);

		if (! (object instanceof Model))
			throw new InvalidSetupException("Input EMF file does not contain a UML model; class was " + object.getClass());

		return (Model) object;
	}

	private static URI convertToURI(Object file) {

		/*
		 * Do not use paths relative to workspace - when NOT using absolute
		 * URLs, the Stereotypes in the model are not resolved (probably because
		 * the stereotype file is not found).
		 */

		URI fileURI= null;
		if (file instanceof IFile)
			fileURI= URI.createFileURI( ((IFile) file).getLocation().toString());
		else
			fileURI= URI.createFileURI( ((File) file).getAbsolutePath());

		return fileURI;
	}

	/**
	 * Find classes and activities, even when "hidden" inside packages or
	 * components
	 * 
	 * @param aContainer
	 * @param activities
	 */
	public static Map<Class, List<Activity>> getParticipantsAndActivities(Model umlModel) {
		Map<Class, List<Activity>> activities= new HashMap<Class, List<Activity>>();
		retrieveInput(umlModel, activities);
		return activities;
	}


	public static void retrieveInput(EObject aContainer, Map<Class, List<Activity>> participantActivities) {

		EList<EObject> contents= aContainer.eContents();
		for (EObject object : contents) {
			if (object instanceof Package || object instanceof Component)
				retrieveInput(object, participantActivities);
			else {
				if (object instanceof Class) {
					Class umlClass= (Class) object;
					if (StereotypeUtil.hasStereotype(umlClass, StereoType.PARTICIPANT)) {
						List<Activity> activities= new LinkedList<Activity>();

						for (Behavior behavior : umlClass.getOwnedBehaviors()) {
							if (behavior instanceof Activity) {
								activities.add((Activity) behavior);
							}
						}
						participantActivities.put(umlClass, activities);
					}
				}
			}
		}
	}

	public static void storeSMM(Object file, EObject ism) throws InvalidSetupException {

		URI inputURI= convertToURI(file);

		ResourceSet resourceSet= new ResourceSetImpl();
		addHeadlessInformation(resourceSet);

		XMIResource xmlResource= (XMIResource) resourceSet.createResource(inputURI);
		xmlResource.getContents().add(ism);
		try {
			xmlResource.save(null);
		} catch (IOException e) {
			throw new InvalidSetupException("Unable to write XMI file: " + e.getMessage());
		}
	}


	private static void addHeadlessInformation(ResourceSet resourceSet) {
		// Headless?
		if (UML2SMM2Plugin.getDefault() == null || UML2SMM2Plugin.getDefault().getBundle() == null) {

			resourceSet.getPackageRegistry().put(UMLPackage.eNS_URI, UMLPackage.eINSTANCE);
			resourceSet.getPackageRegistry().put("http://www.eclipse.org/uml2/2.0.0/UML", UMLPackage.eINSTANCE);

			resourceSet.getResourceFactoryRegistry().getContentTypeToFactoryMap()
					.put("org.eclipse.emf.ecore:contentType", new XMLContentHandlerImpl());
			resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(UMLResource.FILE_EXTENSION, UMLResource.Factory.INSTANCE);

			Registry registry= resourceSet.getResourceFactoryRegistry();

			registry.getExtensionToFactoryMap().put("ecore", new EcoreResourceFactoryImpl());
			registry.getExtensionToFactoryMap().put("xml", new XMLResourceFactoryImpl());
			registry.getExtensionToFactoryMap().put("xmi", new XMIResourceFactoryImpl());

			// For some unknown reason, we need the absolute path to the
			// eclipse uml2 uml resources JAR, which HAS to be in the
			// classpath!!!
			String property= System.getProperties().getProperty("java.class.path", "");
			StringTokenizer st= new StringTokenizer(property, ";");
			String path= null;
			while (st.hasMoreElements()) {
				String next= st.nextToken();
				if (next.contains("uml2.uml.resources")) {
					path= next;
					break;
				}
			}

			if (path == null)
				throw new RuntimeException("Could not find uml2 resources JAR in classpath. Include the jar in the classpath.");

			path= path.replace("\\", "/");

			Map<URI, URI> uriMap= resourceSet.getURIConverter().getURIMap();
			URI uri= URI.createURI("jar:file:/" + path + "!/");
			uriMap.put(URI.createURI(UMLResource.LIBRARIES_PATHMAP), uri.appendSegment("libraries").appendSegment(""));
			uriMap.put(URI.createURI(UMLResource.METAMODELS_PATHMAP), uri.appendSegment("metamodels").appendSegment(""));
			uriMap.put(URI.createURI(UMLResource.PROFILES_PATHMAP), uri.appendSegment("profiles").appendSegment(""));
		}
	}


}
