/*
 * 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.u2m.test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import java.util.Set;

import net.miowb.model.mio.ModalIOAutomaton;

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.EPackage;
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.XMIResourceFactoryImpl;
import org.eclipse.emf.ecore.xmi.impl.XMLContentHandlerImpl;
import org.eclipse.uml2.uml.UMLPackage;

import eu.mdd4soa.smm.behaviour.Compensate;
import eu.mdd4soa.smm.behaviour.CompensateAll;
import eu.mdd4soa.smm.behaviour.CompensationHandler;
import eu.mdd4soa.smm.behaviour.DataHandling;
import eu.mdd4soa.smm.behaviour.Decision;
import eu.mdd4soa.smm.behaviour.ISMBehaviourFactory;
import eu.mdd4soa.smm.behaviour.Loop;
import eu.mdd4soa.smm.behaviour.Parallel;
import eu.mdd4soa.smm.behaviour.Path;
import eu.mdd4soa.smm.behaviour.Receive;
import eu.mdd4soa.smm.behaviour.Reply;
import eu.mdd4soa.smm.behaviour.Send;
import eu.mdd4soa.smm.behaviour.SendAndReceive;
import eu.mdd4soa.smm.behaviour.ServiceActivity;
import eu.mdd4soa.smm.behaviour.ServiceElement;
import eu.mdd4soa.smm.behaviour.Throw;
import eu.mdd4soa.smm.statik.ExceptionType;
import eu.mdd4soa.smm.statik.ISMStatikFactory;
import eu.mdd4soa.smm.statik.InterfaceOperation;
import eu.uml4soa.utbm.u2m.U2MPlugin;
import eu.uml4soa.utbm.u2m.model.CAutomaton;
import eu.uml4soa.utbm.u2m.model.CState;
import eu.uml4soa.utbm.u2m.model.CTransition;

/**
 * 
 * This class contains helper functions for use in the test cases.
 * 
 * @author Philip Mayer, mayer@pst.ifi.lmu.de
 * 
 */
public abstract class AbstractSemanticTests {

	protected String fDirectory;

	protected ServiceElement createCompensateAll() {
		CompensateAll call= ISMBehaviourFactory.eINSTANCE.createCompensateAll();
		call.setName("CompensateAll");
		return call;
	}

	protected ServiceElement createCompensate(ServiceActivity target) {
		Compensate call= ISMBehaviourFactory.eINSTANCE.createCompensate();
		call.setName(target.getName());
		call.setTarget(target);
		return call;
	}

	protected ServiceElement createData(String string) {
		DataHandling dh= ISMBehaviourFactory.eINSTANCE.createDataHandling();
		dh.setName(string);
		return dh;
	}

	protected ServiceActivity createActivity(String name) {
		ServiceActivity sa= ISMBehaviourFactory.eINSTANCE.createServiceActivity();
		sa.setName(name);
		return sa;
	}

	protected ServiceActivity createActivity(String name, ServiceElement... elements) {
		ServiceActivity sa= ISMBehaviourFactory.eINSTANCE.createServiceActivity();
		sa.setName(name);
		for (ServiceElement serviceElement : elements) {
			sa.getChildren().add(serviceElement);
		}
		return sa;
	}

	protected Send createSend(String operationName) {

		Send aSend= ISMBehaviourFactory.eINSTANCE.createSend();
		aSend.setName(operationName + "_Activity");
		InterfaceOperation a= ISMStatikFactory.eINSTANCE.createInterfaceOperation();
		a.setName(operationName);
		aSend.setOperation(a);

		return aSend;
	}

	protected Reply createReply(String operationName) {

		Reply aReply= ISMBehaviourFactory.eINSTANCE.createReply();
		aReply.setName(operationName + "_Activity");
		InterfaceOperation a= ISMStatikFactory.eINSTANCE.createInterfaceOperation();
		a.setName(operationName);
		aReply.setOperation(a);

		return aReply;
	}

	protected Receive createReceive(String operationName) {

		Receive aReceive= ISMBehaviourFactory.eINSTANCE.createReceive();
		aReceive.setName(operationName + "_Activity");
		InterfaceOperation a= ISMStatikFactory.eINSTANCE.createInterfaceOperation();
		a.setName(operationName);
		aReceive.setOperation(a);

		return aReceive;
	}

	protected SendAndReceive createSendAndReceive(String operationName) {

		SendAndReceive aSendAndReceive= ISMBehaviourFactory.eINSTANCE.createSendAndReceive();
		aSendAndReceive.setName(operationName + "_Activity");
		InterfaceOperation a= ISMStatikFactory.eINSTANCE.createInterfaceOperation();
		a.setName(operationName);
		aSendAndReceive.setOperation(a);

		return aSendAndReceive;
	}

	protected Throw createThrow(String exceptionName) {

		ExceptionType exceptionType= ISMStatikFactory.eINSTANCE.createExceptionType();
		exceptionType.setName(exceptionName);


		Throw aThrow= ISMBehaviourFactory.eINSTANCE.createThrow();
		aThrow.setName(exceptionName);
		aThrow.setExceptionType(exceptionType);

		return aThrow;
	}

	protected Decision createDecision(ServiceElement left, ServiceElement right) {

		Decision d= ISMBehaviourFactory.eINSTANCE.createDecision();
		d.setName("Decision");
		Path p1= ISMBehaviourFactory.eINSTANCE.createPath();
		p1.getChildren().add(left);
		d.getChildren().add(p1);

		Path p2= ISMBehaviourFactory.eINSTANCE.createPath();
		p2.getChildren().add(right);
		d.getChildren().add(p2);

		return d;
	}

	protected Parallel createParallel(ServiceElement left, ServiceElement right) {

		Parallel d= ISMBehaviourFactory.eINSTANCE.createParallel();
		d.setName("Decision");
		Path p1= ISMBehaviourFactory.eINSTANCE.createPath();
		p1.getChildren().add(left);
		d.getChildren().add(p1);

		Path p2= ISMBehaviourFactory.eINSTANCE.createPath();
		p2.getChildren().add(right);
		d.getChildren().add(p2);

		return d;
	}

	protected Loop createLoop(ServiceElement... elements) {

		Loop loop= ISMBehaviourFactory.eINSTANCE.createLoop();
		loop.setName("Loop");
		Path p1= ISMBehaviourFactory.eINSTANCE.createPath();
		loop.getChildren().add(p1);

		for (ServiceElement serviceElement : elements) {
			p1.getChildren().add(serviceElement);
		}

		return loop;
	}

	protected CompensationHandler createCompensationHandler(String handlerName, ServiceElement element) {
		CompensationHandler h= ISMBehaviourFactory.eINSTANCE.createCompensationHandler();
		h.setName(handlerName);
		h.getChildren().add(element);
		return h;
	}


	protected CState getFirstAndOnly(List<CState> endStates) {
		return endStates.iterator().next();
	}

	protected void checkStateMatch(String message, String expectedName, CState theState) {
		// Remove all the numbers
		String name= removeNumbers(theState.getName());
		assertEquals(message, expectedName, name);
	}

	protected void checkStateMatch(String message, String expectedName, Set<CState> theState) {

		boolean found= false;
		for (CState cState : theState) {
			String name= removeNumbers(cState.getName());
			if (name.equals(expectedName)) {
				found= true;
				break;
			}
		}

		assertTrue(message, found);
	}

	private String removeNumbers(String oldName) {
		String name= oldName.replaceAll("\\(\\d\\)", "");
		return name;
	}

	protected void ensureTransition(String message, String name, String fromState, String toState, Set<CTransition> transitions) {

		boolean found= false;

		for (CTransition cTransition : transitions) {
			if (removeNumbers(cTransition.getActionLabel()).equals(name) && (removeNumbers(cTransition.getFrom().getName()).equals(fromState))
					&& (removeNumbers(cTransition.getTo().getName()).equals(toState))) {
				found= true;
				break;
			}
		}

		assertTrue(message, found);
	}

	protected static URI convertToURI(Object file) {

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

		return fileURI;
	}

	public static File store(String nameOfFile, CAutomaton c) throws Exception {
		return store(new File(new File("."), nameOfFile + ".mio"), c.toEMF());
	}

	public static File store(File theFile, ModalIOAutomaton prune) throws Exception {
		URI inputURI= convertToURI(theFile);

		ResourceSet resourceSet= new ResourceSetImpl();

		// Headless?
		if (U2MPlugin.getDefault() == null || U2MPlugin.getDefault().getBundle() == null) {
			resourceSet.getResourceFactoryRegistry().getContentTypeToFactoryMap()
					.put("org.eclipse.emf.ecore:contentType", new XMLContentHandlerImpl());
			Registry registry= resourceSet.getResourceFactoryRegistry();
			registry.getExtensionToFactoryMap().put("mio", new XMIResourceFactoryImpl());
		}

		XMIResource xmlResource= (XMIResource) resourceSet.createResource(inputURI);

		xmlResource.getContents().add(prune);
		try {
			xmlResource.save(null);
		} catch (IOException e) {
			throw new Exception("Unable to write XMI file: " + e.getMessage(), e);
		}

		return theFile;
	}

	public static EObject loadUML(File theFile) throws Exception {
		URI inputURI= convertToURI(theFile);

		ResourceSet resourceSet= new ResourceSetImpl();

		// Standalone use:
		EPackage.Registry.INSTANCE.put("http://www.eclipse.org/uml2/2.0.0/UML", UMLPackage.eINSTANCE);

		// Headless?
		if (U2MPlugin.getDefault() == null || U2MPlugin.getDefault().getBundle() == null) {
			resourceSet.getResourceFactoryRegistry().getContentTypeToFactoryMap()
					.put("org.eclipse.emf.ecore:contentType", new XMLContentHandlerImpl());
			Registry registry= resourceSet.getResourceFactoryRegistry();
			registry.getExtensionToFactoryMap().put("uml", new XMIResourceFactoryImpl());

		}

		XMIResource xmlResource= (XMIResource) resourceSet.getResource(inputURI, true);

		EList<EObject> contents= xmlResource.getContents();
		EObject eObject= contents.get(0);
		return eObject;

	}

	protected void compareMios(CAutomaton c, String fileName) throws Exception {
		File justNewlyGenerated= store(new File(new File("."), "resources\\temp\\" + fileName + ".mio"), c.toEMF());
		File theCorrectFile= new File(new File("."), "resources\\correct\\" + fileName + ".mio");

		String correctString= loadIntoString(theCorrectFile);
		String generatedString= loadIntoString(justNewlyGenerated);

		justNewlyGenerated.deleteOnExit();
		justNewlyGenerated.delete();

		assertEquals("Content must match", correctString, generatedString);
	}

	private String loadIntoString(File fileToTest) throws FileNotFoundException, IOException {
		BufferedReader fileToTestReader= new BufferedReader(new FileReader(fileToTest));
		StringBuilder completeFileToTest= new StringBuilder();
		String fileToTestLine= null;
		while ( (fileToTestLine= fileToTestReader.readLine()) != null) {
			completeFileToTest.append(fileToTestLine + "\n");
		}
		fileToTestReader.close();

		return completeFileToTest.toString();
	}

}
