/*******************************************************************************
 * Copyright (c) 2015, 2016 CNRS, IRIT, IRT AESE (IRT Saint Exupéry).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the LGPL 3.0
 * which accompanies this distribution, and is available at
 * https://www.gnu.org/licenses/lgpl-3.0-standalone.html
 *
 * Contributors:
 *     Faiez Zalila - initial API and implementation and/or initial documentation
 *     Jorge Enrique Gutierrez Zuluaga
 *******************************************************************************/
package com.irtsaintexupery.fiacre.simulator.core;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.CommonPlugin;
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.impl.XMIResourceFactoryImpl;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.ui.resource.XtextResourceSetProvider;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import com.google.inject.Injector;
import com.irtsaintexupery.fiacre.instance.fiacreinstance.ComponentDeclarationInstance;
import com.irtsaintexupery.fiacre.instance.fiacreinstance.CompositionInstance;
import com.irtsaintexupery.fiacre.instance.fiacreinstance.EnabledStatementBlock;
import com.irtsaintexupery.fiacre.instance.fiacreinstance.ExecutedStatementBlock;
import com.irtsaintexupery.fiacre.instance.fiacreinstance.FiacreinstanceFactory;
import com.irtsaintexupery.fiacre.instance.fiacreinstance.FiacreinstancePackage;
import com.irtsaintexupery.fiacre.instance.fiacreinstance.FireableStatementBlock;
import com.irtsaintexupery.fiacre.instance.fiacreinstance.InstanceEnabledStatementBlock;
import com.irtsaintexupery.fiacre.instance.fiacreinstance.InstanceExecutedStatementBlock;
import com.irtsaintexupery.fiacre.instance.fiacreinstance.InstanceFireableStatementBlock;
import com.irtsaintexupery.fiacre.instance.fiacreinstance.InstanceModel;
import com.irtsaintexupery.fiacre.instance.fiacreinstance.ProcessDeclarationInstance;
import com.irtsaintexupery.fiacre.instance.fiacreinstance.VariableDeclarationInstance;
import com.irtsaintexupery.fiacre.simulator.Activator;
import com.irtsaintexupery.fiacre.simulator.msc.msc.EnterStateStep;
import com.irtsaintexupery.fiacre.simulator.msc.msc.MSC;
import com.irtsaintexupery.fiacre.simulator.msc.msc.MscFactory;
import com.irtsaintexupery.fiacre.simulator.msc.msc.MscPackage;
import com.irtsaintexupery.fiacre.simulator.msc.msc.SendStep;
import com.irtsaintexupery.fiacre.simulator.msc.msc.Site;
import com.irtsaintexupery.fiacre.simulator.msc.msc.Step;
import com.irtsaintexupery.fiacre.simulator.msc.msc.SynchronisationStep;

import fr.irit.fiacre.xtext.fiacre.ArrayExpression;
import fr.irit.fiacre.xtext.fiacre.ConstructionExpression;
import fr.irit.fiacre.xtext.fiacre.FalseLiteral;
import fr.irit.fiacre.xtext.fiacre.FiacreFactory;
import fr.irit.fiacre.xtext.fiacre.FieldExpression;
import fr.irit.fiacre.xtext.fiacre.IdentifierExpression;
import fr.irit.fiacre.xtext.fiacre.NaturalLiteral;
import fr.irit.fiacre.xtext.fiacre.QueueExpression;
import fr.irit.fiacre.xtext.fiacre.RecordExpression;
import fr.irit.fiacre.xtext.fiacre.TrueLiteral;
import fr.irit.fiacre.xtext.ui.internal.XtextActivator;
import fr.laas.libtts.Tools;

public class Utils {

	private static FiacreinstanceFactory fiacre_instance_factory;
	private static MscFactory fiacre_msc_factory;
	private static FiacreFactory fiacre_factory = FiacreFactory.eINSTANCE;
	//private static String FRAC_PATH = "/home/faiez.zalila/.local/bin/frac-2.5.0/"; 

	public static VariableDeclarationInstance createVariableDeclarationInstance() {
		return fiacre_instance_factory.createVariableDeclarationInstance();
	}

	public static TrueLiteral createTrueLiteral() {
		return fiacre_factory.createTrueLiteral();
	}

	public static FalseLiteral createFalseLiteral() {
		return fiacre_factory.createFalseLiteral();
	}

	public static NaturalLiteral createNaturalLiteral() {
		return fiacre_factory.createNaturalLiteral();
	}

	public static IdentifierExpression createIdentifierExpression() {
		return fiacre_factory.createIdentifierExpression();
	}

	public static ComponentDeclarationInstance createComponentDeclarationInstance() {
		return fiacre_instance_factory.createComponentDeclarationInstance();
	}
	public static CompositionInstance createCompositionInstance() {
		return fiacre_instance_factory.createCompositionInstance();
	}

	public static ProcessDeclarationInstance createProcessDeclarationInstance() {
		return fiacre_instance_factory.createProcessDeclarationInstance();
	}

	public static InstanceExecutedStatementBlock createInstanceExecutedStatementBlock() {
		return fiacre_instance_factory.createInstanceExecutedStatementBlock();
	}

	public static InstanceFireableStatementBlock createInstanceFireableStatementBlock() {
		return fiacre_instance_factory.createInstanceFireableStatementBlock();
	}

	public static InstanceEnabledStatementBlock createInstanceEnabledStatementBlock() {
		return fiacre_instance_factory.createInstanceEnabledStatementBlock();
	}

	public static ExecutedStatementBlock createExecutedStatementBlock() {
		return fiacre_instance_factory.createExecutedStatementBlock();
	}

	public static FireableStatementBlock createFireableStatementBlock() {
		return fiacre_instance_factory.createFireableStatementBlock();
	}

	public static EnabledStatementBlock createEnabledStatementBlock() {
		return fiacre_instance_factory.createEnabledStatementBlock();
	}

	
	public static Site createSite() {
		return fiacre_msc_factory.createSite();
	}
	public static Step createStep() {
		return fiacre_msc_factory.createStep();
	}
	public static SynchronisationStep createSynchronisationStep() {
		return fiacre_msc_factory.createSynchronisationStep();
	}
	public static SendStep createSendStep() {
		return fiacre_msc_factory.createSendStep();
	}
	public static EnterStateStep createEnterStateStep() {
		return fiacre_msc_factory.createEnterStateStep();
	}
	protected static Resource createInstanceModel(IFile file) {
		// Initialize the model
		FiacreinstancePackage.eINSTANCE.eClass();

		// Retrieve the default factory singleton
		fiacre_instance_factory = FiacreinstanceFactory.eINSTANCE;
		// create the content of the model via this program
		InstanceModel instanceModel = fiacre_instance_factory.createInstanceModel();

		// As of here we preparing to save the model content

		// Register the XMI resource factory for the .website extension

		Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
		Map<String, Object> m = reg.getExtensionToFactoryMap();
		m.put("fiacreinstance", new XMIResourceFactoryImpl());

		// Obtain a new resource set
		ResourceSet resSet = new ResourceSetImpl();
		// create a resource
		Resource resource = resSet.createResource(URI.createFileURI(getInstanceFilePath(file)));
		// Get the first model element and cast it to the right type, in my
		// example everything is hierarchical included in this first node
		resource.getContents().add(instanceModel);

		// now save the content.
		try {
			resource.save(Collections.EMPTY_MAP);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return resource;
	}

	protected static Resource createMSCModel(IFile file) {
		// Initialize the model
		MscPackage.eINSTANCE.eClass();

		// Retrieve the default factory singleton
		fiacre_msc_factory = MscFactory.eINSTANCE;
		// create the content of the model via this program
		MSC mscModel = fiacre_msc_factory.createMSC();

		// As of here we preparing to save the model content

		// Register the XMI resource factory for the .website extension

		Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
		Map<String, Object> m = reg.getExtensionToFactoryMap();
		m.put("mscmodel", new XMIResourceFactoryImpl());

		// Obtain a new resource set
		ResourceSet resSet = new ResourceSetImpl();
		// create a resource
		Resource resource = resSet.createResource(URI.createFileURI(getMscFilePath(file)));
		// Get the first model element and cast it to the right type, in my
		// example everything is hierarchical included in this first node
		resource.getContents().add(mscModel);

		// now save the content.
		try {
			resource.save(Collections.EMPTY_MAP);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return resource;
	}
	protected static String nameWithoutExtension(IFile file) {
		String fileName = file.getName();
		int pos = fileName.lastIndexOf(".");
		if (pos > 0) {
			fileName = fileName.substring(0, pos);
		}
		return fileName;
	}

	protected static String getTTSFilePath(IFile file) {
		return file.getParent().getLocation() + "/" + nameWithoutExtension(file) + ".tts";
	}

	protected static String getInstanceFilePath(IFile file) {
		return file.getParent().getLocation() + "/" + nameWithoutExtension(file) + ".fiacreinstance";
	}
	protected static String getMscFilePath(IFile file) {
		return file.getParent().getLocation() + "/" + nameWithoutExtension(file) + ".mscmodel";
	}

	protected static void callFracCommand(IFile file) {
		try {
			ProcessBuilder pb_make = new ProcessBuilder(Tools.FRAC_PATH+"frac","-g", "-j", file.getLocation().toString(), 
					getTTSFilePath(file));
			pb_make.directory(new File(file.getParent().getLocation().toString()));
			Process p = pb_make.start();
			p.waitFor();
			System.out.println("Compiling Fiacre model ...");
		} catch (IOException e) {
			e.printStackTrace();

		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
	}
	
	protected static void callFrac_G_Command(IFile file) {
		try {
			ProcessBuilder pb_make = new ProcessBuilder(Tools.FRAC_PATH+"frac", "-G", file.getLocation().toString(),
					getTTSFilePath(file)+"/"+file.getName()+".fcriG");
			pb_make.directory(new File(file.getParent().getLocation().toString()));
			Process p = pb_make.start();
			p.waitFor();
		} catch (IOException e) {
			e.printStackTrace();

		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
	}
	
	protected static void callFrac_g_G_Command(IFile file) {
		try {
			ProcessBuilder pb_make = new ProcessBuilder(Tools.FRAC_PATH+"frac","-g","-G", file.getLocation().toString(),
					getTTSFilePath(file)+"/"+file.getName()+".fcrigG");
			pb_make.directory(new File(file.getParent().getLocation().toString()));
			Process p = pb_make.start();
			p.waitFor();
		} catch (IOException e) {
			e.printStackTrace();

		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
	}

	protected static void callPythonCommand(IFile file) {
		try {
			ProcessBuilder pb_make = new ProcessBuilder("python", "-m", "json.tool",
					nameWithoutExtension(file) + ".json", "f_" + nameWithoutExtension(file) + ".json");
			pb_make.directory(new File(getTTSFilePath(file)));
			Process p = pb_make.start();
			p.waitFor();
		} catch (IOException e) {
			e.printStackTrace();

		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
	}

	protected static void callMakeCommand(IFile file) {
		try {
			ProcessBuilder pb_make = new ProcessBuilder("make", "-f", Tools.FRAC_PATH+"Makefile",
					" NAME=" + nameWithoutExtension(file),"FRACLIB="+Tools.FRAC_PATH+"lib");
			pb_make.directory(new File(file.getParent().getLocation().toString()));
			Process p = pb_make.start();
			p.waitFor();
			System.out.println("Compiled Fiacre model");
		} catch (IOException e) {
			e.printStackTrace();

		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
	}

	protected static JSONObject getStaticJsonObject(String static_json_file_path) {
		JSONParser parser = new JSONParser();
		try {
			FileReader fr = new FileReader(static_json_file_path);
			Object obj = parser.parse(fr);
			// The most important statement
			// It is necessary to close the FileReader in order to
			// unlock the file locked during its opening
			fr.close();
			return (JSONObject) obj;
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	public static JSONObject getJsonObject(String string) {
		JSONParser parser = new JSONParser();
		try {
			Object obj = parser.parse(string);
			return (JSONObject) obj;
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	protected static String getStaticJsonFilePath(IFile file) {
		return getTTSFilePath(file) + "/" + nameWithoutExtension(file) + ".json";
	}

	protected static XtextResource getFiacreXtextResource(IFile file) {
		Injector injector = XtextActivator.getInstance().getInjector("fr.irit.fiacre.xtext.Fiacre");
		XtextResourceSetProvider provider = injector.getInstance(XtextResourceSetProvider.class);
		ResourceSet resourceSet = provider.get(file.getProject());

		URI xtextURI = URI.createPlatformResourceURI(file.getFullPath().toString(), true);

		return (XtextResource) resourceSet.getResource(xtextURI, true);

	}

	public static void saveResource(Resource resource) {
		try {
			resource.save(Collections.EMPTY_MAP);
		} catch (IOException e) {
			e.printStackTrace();
		}
		//Added
		URI resolvedFile = CommonPlugin.resolve(resource.getURI());
		IResource dfile = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(resolvedFile.toFileString()));
		try {
			dfile.refreshLocal(IResource.DEPTH_INFINITE, null);
		} catch (CoreException e) {
			e.printStackTrace();
		}
	}

	protected static void compileFiacreModel(IFile file) {
		try {
			Tools.getProperties() ;		
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		// Call Frac compiler
		callFracCommand(file);
		// Call make
		callMakeCommand(file);
		callFrac_g_G_Command(file);
		callFrac_G_Command(file);
	}
	public static QueueExpression createQueueExpression() {
		return fiacre_factory.createQueueExpression();
	}

	public static ArrayExpression createArrayExpression() {
		return fiacre_factory.createArrayExpression();
	}

	public static RecordExpression createRecordExpression() {
		return fiacre_factory.createRecordExpression();
	}

	public static FieldExpression createFieldExpression() {
		return fiacre_factory.createFieldExpression();
	}

	public static ConstructionExpression createConstructionExpression() {
		return fiacre_factory.createConstructionExpression();
	}

	public static void showException(String message, final Exception ex) {

		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw);
		ex.printStackTrace(pw);
		// ex.printStackTrace();

		final String trace = sw.toString(); // stack trace as a string

		// Temp holder of child statuses
		List<Status> childStatuses = new ArrayList<Status>();

		String error = null;
		// Split output by OS-independend new-line
		for (String line : trace.split(System.getProperty("line.separator"))) {
			if (error == null)
				error = line;
			else
				childStatuses.add(new Status(IStatus.ERROR, Activator.PLUGIN_ID, line));
		}

		MultiStatus multiStatus = new MultiStatus(Activator.PLUGIN_ID, IStatus.ERROR,
				childStatuses.toArray(new Status[] {}), // convert to array of
														// statuses
				error, ex);

		ErrorDialog.openError(null, "Unexpected Error", message, multiStatus);
	}
}
