/*******************************************************************************
 * Copyright (c) 2010, 2014 Obeo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Obeo - initial API and implementation
 *******************************************************************************/
package fr.femtost.disc.eclipse.sysml2vhdlams.atl.transformation;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl;
import org.eclipse.m2m.atl.common.ATLExecutionException;
import org.eclipse.m2m.atl.core.ATLCoreException;
import org.eclipse.m2m.atl.core.IExtractor;
import org.eclipse.m2m.atl.core.IInjector;
import org.eclipse.m2m.atl.core.IModel;
import org.eclipse.m2m.atl.core.IReferenceModel;
import org.eclipse.m2m.atl.core.ModelFactory;
import org.eclipse.m2m.atl.core.emf.EMFExtractor;
import org.eclipse.m2m.atl.core.emf.EMFInjector;
import org.eclipse.m2m.atl.core.emf.EMFModelFactory;
import org.eclipse.m2m.atl.core.launch.ILauncher;
import org.eclipse.m2m.atl.engine.emfvm.launch.EMFVMLauncher;

/**
 * Entry point of the 'inModel2outModel' transformation module.
 * @generated not
 */
public class Sysml2vhdlams {

    /**
     * The property file. Stores module list, the metamodel and library locations.
     *
     * @generated not
     */
    private Properties properties;

    /**
	 * The INsysml model.
	 * @generated
	 */
	protected IModel insysmlModel;

				/**
	 * The INuml model.
	 * @generated
	 */
	protected IModel inumlModel;

				/**
	 * The INieeeLib model.
	 * @generated
	 */
	protected IModel inieeelibModel;

				/**
	 * The INStandardLib model.
	 * @generated
	 */
	protected IModel instandardlibModel;

				/**
	 * The OUT model.
	 * @generated
	 */
	protected IModel outModel;

				/**
	 * The main method.
	 * 
	 * @param args
	 *            are the arguments
	 * @generated
	 */
	public static void main(String[] args) {
		try {
			if (args.length < 5) {
				System.out.println("Arguments not valid : {INsysml_model_path, INuml_model_path, INieeeLib_model_path, INStandardLib_model_path, OUT_model_path}.");
			} else {
				Sysml2vhdlams runner = new Sysml2vhdlams();
				runner.loadModels(args[0], args[1], args[2], args[3]);
				runner.doSysml2vhdlams(new NullProgressMonitor());
				runner.saveModels(args[4]);
			}
		} catch (ATLCoreException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ATLExecutionException e) {
			e.printStackTrace();
		}
	}

				/**
     * The inModel model.
     *
     * @generated not
     */
    private IModel sysmlInstance;

    protected IModel umlInstance;

	protected IModel vhdlamsPrimitiveInstance;
	
	protected IModel vhdlamsLibraryInstance;
	
    /**
     * The outModel model.
     *
     * @generated not
     */
    private IModel vhdlamsInstance;

    /**
     * The main method.
     *
     * @param inModel    the input model
     * @param outProblem the output model
     * @throws IOException      if the input model does not exists
     * @throws ATLCoreException if the transformation fails
     * @generated not
     */
    public static void transform(final String inModel, final String outProblem) throws IOException, ATLCoreException {
        Sysml2vhdlams runner = new Sysml2vhdlams();
        runner.loadModels(inModel);
        runner.doinModel2outModel(new NullProgressMonitor());
        runner.saveModels(outProblem);
    }

    /**
     * Constructor.
     *
     * @throws java.io.IOException if the property file does not exists
     * @generated not
     */
    public Sysml2vhdlams() throws IOException {
        properties = new Properties();
        InputStream isProp = getFileURL("Sysml2vhdlams.properties").openStream();
        properties.load(isProp);
        isProp.close();
        Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("ecore", new EcoreResourceFactoryImpl());
    }

    /**
	 * Load the input and input/output models, initialize output models.
	 * 
	 * @param insysmlModelPath
	 *            the INsysml model path
	 * @param inumlModelPath
	 *            the INuml model path
	 * @param inieeelibModelPath
	 *            the INieeeLib model path
	 * @param instandardlibModelPath
	 *            the INStandardLib model path
	 * @throws ATLCoreException
	 *             if a problem occurs while loading models
	 *
	 * @generated
	 */
	public void loadModels(String insysmlModelPath, String inumlModelPath, String inieeelibModelPath, String instandardlibModelPath) throws ATLCoreException {
		ModelFactory factory = new EMFModelFactory();
		IInjector injector = new EMFInjector();
	 	IReferenceModel mmsysmlMetamodel = factory.newReferenceModel();
		injector.inject(mmsysmlMetamodel, getMetamodelUri("MMsysml"));
	 	IReferenceModel mmvhdlamsMetamodel = factory.newReferenceModel();
		injector.inject(mmvhdlamsMetamodel, getMetamodelUri("MMvhdlams"));
	 	IReferenceModel mmumlMetamodel = factory.newReferenceModel();
		injector.inject(mmumlMetamodel, getMetamodelUri("MMuml"));
		this.insysmlModel = factory.newModel(mmsysmlMetamodel);
		injector.inject(insysmlModel, insysmlModelPath);
		this.inumlModel = factory.newModel(mmumlMetamodel);
		injector.inject(inumlModel, inumlModelPath);
		this.inieeelibModel = factory.newModel(mmvhdlamsMetamodel);
		injector.inject(inieeelibModel, inieeelibModelPath);
		this.instandardlibModel = factory.newModel(mmvhdlamsMetamodel);
		injector.inject(instandardlibModel, instandardlibModelPath);
		this.outModel = factory.newModel(mmvhdlamsMetamodel);
	}

				/**
     * Load the input and input/output models, initialize output models.
     *
     * @param inModelPath the inModel model path
     * @throws ATLCoreException if a problem occurs while loading models
     * @generated not
     */
    public final void loadModels(final String inModelPath) throws ATLCoreException {
        ModelFactory factory = new EMFModelFactory();
        IInjector injector = new EMFInjector();
        IReferenceModel sysmlMetamodel = factory.newReferenceModel();
        injector.inject(sysmlMetamodel, getMetamodelUri("MMsysml"));
        IReferenceModel vhdlamsMetamodel = factory.newReferenceModel();
        injector.inject(vhdlamsMetamodel, getMetamodelUri("MMvhdlams"));
        IReferenceModel mmumlMetamodel = factory.newReferenceModel();
		injector.inject(mmumlMetamodel, getMetamodelUri("MMuml"));
		
        this.sysmlInstance = factory.newModel(sysmlMetamodel);
        injector.inject(sysmlInstance, inModelPath);
        this.umlInstance = factory.newModel(mmumlMetamodel);
		injector.inject(umlInstance, inModelPath);
		this.vhdlamsLibraryInstance = factory.newModel(vhdlamsMetamodel);
		injector.inject(vhdlamsLibraryInstance, "pathmap://VHDLAMS_LIBRARIES/ieee.vhdl.xmi");
		this.vhdlamsPrimitiveInstance = factory.newModel(vhdlamsMetamodel);
		injector.inject(vhdlamsPrimitiveInstance, "pathmap://VHDLAMS_LIBRARIES/vhdlPrimitive.vhdl.xmi");
        this.vhdlamsInstance = factory.newModel(vhdlamsMetamodel);
    }

    /**
     * Save the output and input/output models.
     *
     * @param outModelPath the outModel model path
     * @throws ATLCoreException if a problem occurs while saving models
     * @generated not
     */
    public final void saveModels(final String outModelPath) throws ATLCoreException {
        IExtractor extractor = new EMFExtractor();
        extractor.extract(vhdlamsInstance, outModelPath);
    }

    /**
	 * Transform the models.
	 * 
	 * @param monitor
	 *            the progress monitor
	 * @throws ATLCoreException
	 *             if an error occurs during models handling
	 * @throws IOException
	 *             if a module cannot be read
	 * @throws ATLExecutionException
	 *             if an error occurs during the execution
	 *
	 * @generated
	 */
	public Object doSysml2vhdlams(IProgressMonitor monitor) throws ATLCoreException, IOException, ATLExecutionException {
		ILauncher launcher = new EMFVMLauncher();
		List<InputStream> inputStreamsToClose = new ArrayList<InputStream>();
		Map<String, Object> launcherOptions = getOptions();
		launcher.initialize(launcherOptions);
		launcher.addInModel(insysmlModel, "INsysml", "MMsysml");
		launcher.addInModel(inumlModel, "INuml", "MMuml");
		launcher.addInModel(inieeelibModel, "INieeeLib", "MMvhdlams");
		launcher.addInModel(instandardlibModel, "INStandardLib", "MMvhdlams");
		launcher.addOutModel(outModel, "OUT", "MMvhdlams");
		InputStream[] modulesStreams = getModulesList();
		inputStreamsToClose.addAll(Arrays.asList(modulesStreams));
		Object result = launcher.launch("run", monitor, launcherOptions, (Object[]) modulesStreams);
		for (InputStream inputStream : inputStreamsToClose) {
			inputStream.close();
		}
		return result;
	}

				/**
     * Transform the models.
     *
     * @param monitor the progress monitor
     * @return the result of the eclispe execution for the transformation
     * @throws ATLCoreException      if an error occurs during models handling
     * @throws IOException           if a module cannot be read
     * @throws ATLExecutionException if an error occurs during the execution
     * @generated not
     */
    public final Object doinModel2outModel(final IProgressMonitor monitor) throws ATLCoreException, IOException, ATLExecutionException {
        ILauncher launcher = new EMFVMLauncher();
        List<InputStream> inputStreamsToClose = new ArrayList<InputStream>();
        Map<String, Object> launcherOptions = getOptions();
        launcher.initialize(launcherOptions);
        launcher.addInModel(sysmlInstance, "INsysml", "MMsysml");
        launcher.addInModel(umlInstance, "INuml", "MMuml");
        launcher.addInModel(vhdlamsPrimitiveInstance, "INStandardLib", "MMvhdlams"); 
        launcher.addInModel(vhdlamsLibraryInstance, "INieeeLib", "MMvhdlams");
        launcher.addOutModel(vhdlamsInstance, "OUT", "MMvhdlams");
        InputStream[] modulesStreams = getModulesList();
        inputStreamsToClose.addAll(Arrays.asList(modulesStreams));
        Object result = launcher.launch("run", monitor, launcherOptions, (Object[]) modulesStreams);
        for (InputStream inputStream : inputStreamsToClose) {
            inputStream.close();
        }
        return result;
    }

    /**
     * Returns an Array of the module input streams, parameterized by the
     * property file.
     *
     * @return an Array of the module input streams
     * @throws IOException if a module cannot be read
     * @generated not
     */
    protected final InputStream[] getModulesList() throws IOException {
        InputStream[] modules = null;
        String modulesList = properties.getProperty("Sysml2vhdlams.modules");
        if (modulesList != null) {
            String[] moduleNames = modulesList.split(",");
            modules = new InputStream[moduleNames.length];
            for (int i = 0; i < moduleNames.length; i++) {
                String asmModulePath = new Path(moduleNames[i].trim()).removeFileExtension().addFileExtension("asm").toString();
                modules[i] = getFileURL(asmModulePath).openStream();
            }
        }
        return modules;
    }

    /**
     * Returns the URI of the given metamodel, parameterized from the property file.
     *
     * @param metamodelName the metamodel name
     * @return the metamodel URI
     * @generated not
     */
    protected final String getMetamodelUri(final String metamodelName) {
        return properties.getProperty(MessageFormat.format("Sysml2vhdlams.metamodels.{0}", metamodelName));
    }

    /**
     * Returns the file name of the given library, parameterized from the property file.
     *
     * @param libraryName the library name
     * @return the library file name
     * @throws java.io.IOException if the property file does not exists
     * @generated not
     */
    protected final InputStream getLibraryAsStream(final String libraryName) throws IOException {
        return getFileURL(properties.getProperty(MessageFormat.format("Sysml2vhdlams.libraries.{0}", libraryName))).openStream();
    }

    /**
     * Returns the options map, parameterized from the property file.
     *
     * @return the options map
     * @generated not
     */
    protected final Map<String, Object> getOptions() {
        Map<String, Object> options = new HashMap<String, Object>();
        for (Entry<Object, Object> entry : properties.entrySet()) {
            if (entry.getKey().toString().startsWith("Sysml2vhdlams.options.")) {
                options.put(entry.getKey().toString().replaceFirst("Sysml2vhdlams.options.", ""),
                        entry.getValue().toString());
            }
        }
        return options;
    }

    /**
     * Finds the file in the plug-in. Returns the file URL.
     *
     * @param fileName the file name
     * @return the file URL
     * @throws IOException if the file doesn't exist
     * @generated not
     */
    protected static URL getFileURL(final String fileName) throws IOException {
        final URL fileURL;
        if (isEclipseRunning()) {
            URL resourceURL = Sysml2vhdlams.class.getResource(fileName);
            if (resourceURL != null) {
                fileURL = FileLocator.toFileURL(resourceURL);
            } else {
                fileURL = null;
            }
        } else {
            fileURL = Sysml2vhdlams.class.getResource(fileName);
        }
        if (fileURL == null) {
            throw new IOException(MessageFormat.format("''{0}'' not found", fileName));
        } else {
            return fileURL;
        }
    }

    /**
     * Tests if eclipse is running.
     *
     * @return <code>true</code> if eclipse is running
     * @generated not
     */
    public static boolean isEclipseRunning() {
        try {
            return Platform.isRunning();
        } catch (Throwable exception) {
            // Assume that we aren't running.
        }
        return false;
    }
}
