package org.eclipse.epsilon.eol.dom.ast2dom.workbench;

import java.io.File;

import org.eclipse.epsilon.common.parse.problem.ParseProblem;
import org.eclipse.epsilon.common.util.FileUtil;
import org.eclipse.epsilon.emc.emf.EmfUtil;
import org.eclipse.epsilon.emc.emf.InMemoryEmfModel;
import org.eclipse.epsilon.eol.EolModule;
import org.eclipse.epsilon.eol.dom.DomElement;
import org.eclipse.epsilon.eol.dom.EolPackage;
import org.eclipse.epsilon.eol.dom.ast2dom.Ast2DomContext;
import org.eclipse.epsilon.eol.dom.printer.EolElementPrinterFactory;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.models.IModel;

public class RefactoringWorkbench {
	
	public static void main(String[] args) throws Exception {	
		String inputProgram = "test1.eol";
		String refactoringScript = "existsRefactor.eol";
		
		// 0. Calculate other file names from the inputProgram
		String inputModel = inputProgram + ".xmi";
		String refactoredModel = inputProgram + ".refactored.xmi";
		String outputProgram = inputProgram + ".refactored.eol";
		
		IModel program = null;
		RefactoringWorkbench workbench = new RefactoringWorkbench();
		
		// 1. Read in the EOL source code and convert it to a model
		try {
			String eolSource = workbench.readNearbyFile(inputProgram);
			program = workbench.convertEolSourceToModel(eolSource);
		} catch (Exception e) {
			System.err.println("Error encountered whilst parsing input program.");
			System.err.println("Exiting with no output.");
			System.exit(1);
		}

		// 1a. Store the original model in case we want to inspect it
		workbench.writeModelToNearbyPath(inputModel, program);
		
		// 2. Run the refactoring script(s) on the model
		try {
			File script = workbench.getNearbyFile(refactoringScript);
			workbench.runRefactoring(script, program);
		} catch (Exception e) {
			System.err.println("Error encountered whilst running refactoring '" + refactoringScript + "'.");
			System.err.println("The EOL model is available at: " + inputModel);
			System.err.println(e);
			e.printStackTrace();
			System.exit(1);
		}
		
		// 2a. Store the refactored model in case we want to inspect it
		workbench.writeModelToNearbyPath(refactoredModel, program);
		
		// 3. Convert the refactored model to source code and save to a file
		String refactoredEolSource = "";
		try {
			refactoredEolSource = workbench.convertEolModelToSource(program);
			workbench.writeNearbyFile(outputProgram, refactoredEolSource);
		} catch (Exception e) {
			System.err.println("Error encountered whilst pretty printing the refactored model.");
			System.err.println("The input EOL model is available at: " + inputModel);
			System.err.println("The output EOL model is available at: " + refactoredModel);
			System.err.println(e);
			e.printStackTrace();
			System.exit(1);
		}
		
		System.out.println("Refactoring complete.");
		System.out.println("The input EOL model is available at: " + inputModel);
		System.out.println("The output EOL model is available at: " + refactoredModel);
		System.out.println("The refactored EOL program is below, and also available at: " + outputProgram);
		System.out.println();
		System.out.println(refactoredEolSource);
	}

	public IModel convertEolSourceToModel(String eolSource) throws Exception {
		EolModule eolModule = new EolModule();
		eolModule.parse(eolSource);
		
		if (!eolModule.getParseProblems().isEmpty()) {
			for (ParseProblem p : eolModule.getParseProblems()) {
				System.err.println(p);
			}
			throw new EolRuntimeException("Could not parse: " + eolSource);
		}

		Ast2DomContext context = new Ast2DomContext();
		DomElement dom = context.getEolElementCreatorFactory().createDomElement(eolModule.getAst(), null, context);
		
		return new InMemoryEmfModel("Program", EmfUtil.createResource(dom), EolPackage.eINSTANCE);
	}
	
	public void runRefactoring(File script, IModel program) throws Exception {
		EolModule module = new EolModule();
		module.getContext().getModelRepository().addModel(program);
		module.parse(script);
		module.execute();
	}
	
	public String convertEolModelToSource(IModel eolModel) {
		DomElement firstModelElement = (DomElement) eolModel.allContents().iterator().next();
		return new EolElementPrinterFactory().print(firstModelElement);
	}
	
	public String readNearbyFile(String relativePath) throws Exception {
		return FileUtil.getFileContents(getNearbyFile(relativePath));
	}
	
	public void writeNearbyFile(String relativePath, String contents) throws Exception {
		FileUtil.setFileContents(contents, getNearbyFile(relativePath));
	}

	public File getNearbyFile(String relativePath) {
		return FileUtil.getFile(relativePath, RefactoringWorkbench.class);
	}
	
	public void writeModelToNearbyPath(String relativePath, IModel program) {
		String path = FileUtil.getPath(relativePath, RefactoringWorkbench.class);
		program.store("file://" + path.replace("bin", "src"));
	}
}