/**
 * File:    ATLTransformation.java
 * Created: 09.01.2009
 *
/*******************************************************************************
 * Copyright (c) 2005-2011
 * University of Augsburg, Germany <www.ds-lab.org>
 *
 * 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:
 *    Florian Lautenbacher, Programming distributed Systems Lab, University of Augsburg
 *    Stephane Drapeau, Obeo (provided the sample Java implementation for starting ATL) 
 *******************************************************************************/
package org.eclipse.jwt.converter.internal;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.emf.common.util.URI;
import org.eclipse.jwt.converter.PluginProperties;
import org.eclipse.jwt.converter.internal.exception.UnsupportedVersionException;
import org.eclipse.m2m.atl.drivers.emf4atl.ASMEMFModel;
import org.eclipse.m2m.atl.drivers.emf4atl.AtlEMFModelHandler;
import org.eclipse.m2m.atl.drivers.emf4atl.EMFModelLoader;
import org.eclipse.m2m.atl.engine.vm.AtlLauncher;
import org.eclipse.m2m.atl.engine.vm.AtlModelHandler;

public class ATLTransformation
{

	private String jwtNameMetamodelFile = "JWT";

	private static ATLTransformation instance = null;

	private final static String metamodelPath = "/org/eclipse/jwt/converter/internal/resources/metamodels/";

	private static AtlEMFModelHandler modelHandler;
	private static EMFModelLoader modelLoaderSource;
	private static EMFModelLoader modelLoaderTarget;

	private ASMEMFModel jwtSourceMetamodel;
	private ASMEMFModel jwtTargetMetamodel;
	private ASMEMFModel jwtTargetViewMetamodel;

	private URL JWT2JWT_TransfoResource;
	// if no source or targetVersion is supported then an
	// UnsupportedVersionException is
	// thrown!
	private String targetVersionString = "";
	private int targetVersion = -1;
	private int sourceVersion = -1;


	/**
	 * Singleton constructor
	 * 
	 * @return the instance of the ATLTransformation
	 */
	static public ATLTransformation getInstance()
	{
		if (instance == null)
		{
			instance = new ATLTransformation();
		}
		return instance;
	}


	/**
	 * creates the source resource that is needed for the conversion
	 * 
	 * @throws UnsupportedVersionException
	 * @throws IOException
	 */
	private void createResources(Map<String, Object> models)
			throws UnsupportedVersionException, IOException
	{

		if (sourceVersion == -1)
		{
			throw new UnsupportedVersionException("No source version specified!");
		}

		// create streams
		InputStream sourceMetaModelStream = getClass().getResourceAsStream(
				metamodelPath + jwtNameMetamodelFile + sourceVersion + ".ecore");//$NON-NLS-1$
		InputStream targetMetaModelStream = getClass().getResourceAsStream(
				metamodelPath + jwtNameMetamodelFile + targetVersion + ".ecore");//$NON-NLS-1$
		InputStream targetMetaModelViewStream = getClass().getResourceAsStream(
				metamodelPath + jwtNameMetamodelFile + "View" + targetVersion + ".ecore");//$NON-NLS-1$

		// load meta models
		jwtSourceMetamodel = (ASMEMFModel) modelLoaderSource.loadModel(
				"jwtSource", modelLoaderSource.getMOF(), sourceMetaModelStream);//$NON-NLS-1$

		jwtTargetMetamodel = (ASMEMFModel) modelLoaderTarget.loadModel(
				"jwtTarget", modelLoaderTarget.getMOF(), targetMetaModelStream);//$NON-NLS-1$

		modelLoaderTarget.getResourceSet().getResource(URI.createURI("jwtTarget"), false)
				.setURI(URI.createURI(jwtNameMetamodelFile + targetVersion + ".ecore"));
		modelLoaderTarget
				.getResourceSet()
				.getResource(
						URI.createURI(jwtNameMetamodelFile + targetVersion + ".ecore"),
						false).setURI(URI.createURI("jwtTarget"));

		// put metamodels in list
		models.put("jwtSource", jwtSourceMetamodel);
		models.put("jwtTarget", jwtTargetMetamodel);

		// only to be executed if a view file shall be created
		if (targetMetaModelViewStream != null)
		{
			jwtTargetViewMetamodel = (ASMEMFModel) modelLoaderTarget
					.loadModel(
							"jwtTargetView", modelLoaderTarget.getMOF(), targetMetaModelViewStream);//$NON-NLS-1$
			models.put("jwtTargetView", jwtTargetViewMetamodel);
		}
	}


	public void jwt2jwt(String inFilePath, String outFilePath) throws IOException,
			UnsupportedVersionException
	{
		modelHandler = (AtlEMFModelHandler) AtlModelHandler
				.getDefault(AtlModelHandler.AMH_EMF);
		modelLoaderSource = (EMFModelLoader) modelHandler.createModelLoader();
		modelLoaderTarget = (EMFModelLoader) modelHandler.createModelLoader();

		// get/create meta models
		Map<String, Object> models = new HashMap<String, Object>();
		createResources(models);

		// get/create models
		ASMEMFModel jwtInputModel = (ASMEMFModel) modelLoaderSource.loadModel(
				"IN", jwtSourceMetamodel, new FileInputStream(inFilePath));//$NON-NLS-1$
		ASMEMFModel jwtOutputModel = (ASMEMFModel) modelLoaderTarget.newModel(
				"OUT", URI.createFileURI(outFilePath).toString(), jwtTargetMetamodel);//$NON-NLS-1$

		// load models
		models.put("IN", jwtInputModel);//$NON-NLS-1$
		models.put("OUT", jwtOutputModel);//$NON-NLS-1$

		ASMEMFModel jwtOutputViewModel = null;
		// only to be executed if a view file shall be created
		if (jwtTargetViewMetamodel != null)
		{
			jwtOutputViewModel = (ASMEMFModel) modelLoaderTarget.newModel("VIEW", URI
					.createFileURI(outFilePath).toFileString(), jwtTargetViewMetamodel);
			models.put("VIEW", jwtOutputViewModel);//$NON-NLS-1$
			jwtOutputViewModel.setCheckSameModel(false);

		}
		// launch
		JWT2JWT_TransfoResource = ATLTransformation.class
				.getResource("resources/transformations/" + "JWT" + sourceVersion
						+ "toJWT" + targetVersion + ".asm");//$NON-NLS-1$

		Map options = new HashMap();

		options.put("allowInterModelReferences", "true");
		jwtInputModel.setCheckSameModel(false);
		jwtOutputModel.setCheckSameModel(false);

		AtlLauncher.getDefault().launch(this.JWT2JWT_TransfoResource,
				Collections.EMPTY_MAP, models, Collections.EMPTY_MAP,
				Collections.EMPTY_LIST, options);

		modelLoaderTarget.save(jwtOutputModel, new File(outFilePath).toURI().toURL()
				.toString());

		// only to be executed if a view file shall be created
		if (jwtOutputViewModel != null)
		{
			modelLoaderTarget.save(jwtOutputViewModel, new File(outFilePath + "_view")
					.toURI().toURL().toString());
		}
		dispose(models);
	}


	/**
	 * Just do a search/replace of the file version if meta model hasn't
	 * changed.
	 * 
	 * @param inFilePath
	 * @param outFilePath
	 * @throws Exception
	 */
	public void justUpdateFileTag(String inFilePath, String outFilePath) throws Exception
	{
		String fileContents = "";
		FileInputStream inputStream = null;
		try
		{
			inputStream = new FileInputStream(inFilePath);
			StringBuilder sb = new StringBuilder();
			String line;

			BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
			while ((line = reader.readLine()) != null)
				sb.append(line).append("\n");

			fileContents = sb.toString();
		}
		catch (Exception e)
		{
			e.printStackTrace();
			throw new Exception(
					PluginProperties.getString("convert_couldnotupdate_message"), e);
		}
		finally
		{
			inputStream.close();
		}

		// look for the fileversion tag
		int versionStringBegin = fileContents.indexOf("fileversion=\"")
				+ "fileversion=\"".length();
		int versionStringEnd = fileContents.indexOf("\"", versionStringBegin);

		String newFileContents = "";
		newFileContents += fileContents.subSequence(0, versionStringBegin);
		newFileContents += targetVersionString;
		newFileContents += fileContents.subSequence(versionStringEnd,
				fileContents.length() - 1);

		// write
		try
		{
			BufferedWriter out = new BufferedWriter(new FileWriter(outFilePath));
			out.write(newFileContents);
			out.close();

		}
		catch (Exception e)
		{
			throw new Exception(
					PluginProperties.getString("convert_couldnotupdate_message"), e);
		}
	}


	/**
	 * sets the source version of the transformation
	 * 
	 * @param version
	 *            the version that the source file and metamodel have
	 */
	public void setSourceVersion(int version)
	{
		this.sourceVersion = version;
	}


	/**
	 * sets the target version of the transformation
	 * 
	 * @param version
	 *            the version that the target file and metamodel have
	 */
	public void setTargetVersion(int version, String versionS)
	{
		this.targetVersion = version;
		this.targetVersionString = versionS;
	}


	private void dispose(Map<String, Object> models)
	{
		models.clear();
		// for (Object model : models.values())
		// ((ASMEMFModel) model).getModelLoader().dispose();
	}

}
