package ua.ansymo.hintco

import java.io.File
import java.io.FileWriter
import org.apache.commons.cli.CommandLine
import org.apache.commons.cli.DefaultParser
import org.apache.commons.cli.HelpFormatter
import org.apache.commons.cli.Option
import org.apache.commons.cli.Options
import org.apache.commons.cli.ParseException
import org.eclipse.core.runtime.Assert
import org.eclipse.equinox.app.IApplication
import org.eclipse.equinox.app.IApplicationContext
import org.intocps.fmi.jnifmuapi.Factory
import ua.ansymo.hintco.RootCandidateScenario

class Main implements IApplication {
	
	def static showHelp(Options options)
	{
		val formatter = new HelpFormatter();
		formatter.printHelp("app", options);
	}
	
	def static void main(String[] args) {
		val helpOpt = Option.builder("h").longOpt("help").desc("Show this description").build();
		val scenarioOpt = Option.builder("s").longOpt("scenario").desc("Path to scenario file").hasArg().numberOfArgs(1).argName("path").build();
		val contractOpt = Option.builder("c").longOpt("contract").desc("Path to contracts file").hasArg().numberOfArgs(1).argName("path").build();
		val genDefAdaptations = Option.builder("d").longOpt("default").desc("Generate default adaptations").build();
		val optLoops = Option.builder("loopOpt").desc("Optimize algebraic loops").build();
		val genTrace = Option.builder("t").longOpt("trace").desc("Generate debug trace.").hasArg().numberOfArgs(1).build();
		val outDirOpt = Option.builder("o").longOpt("output").desc("Path to output directory").hasArg().numberOfArgs(1).argName("path").build();
		val nVarsOpt = Option.builder("n").longOpt("variants").desc("Number of variants to run").hasArg().numberOfArgs(1).argName("int").build();
		
		val options = new Options()
		options.addOption(helpOpt)
		options.addOption(scenarioOpt)
		options.addOption(contractOpt)
		options.addOption(genDefAdaptations)
		options.addOption(optLoops)
		options.addOption(genTrace)
		options.addOption(outDirOpt)
		options.addOption(nVarsOpt)
		
		val parser = new DefaultParser();
		var CommandLine cmd;
		try
		{
			cmd = parser.parse(options, args);
		} catch (ParseException e)
		{
			System.err.println("Parsing failed. Reason: " + e.getMessage());
			showHelp(options);
			return
		}
		
		if(cmd.hasOption(helpOpt.getOpt()))
		{
			showHelp(options);
			return
		}
		
		if (cmd.hasOption(genTrace.getOpt())) {
			System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug")
			val logFile = getOptString(genTrace, cmd, null);
			if (logFile === null){
				System.setProperty("org.slf4j.simpleLogger.logFile", "trace.log")				
			} else {
				System.setProperty("org.slf4j.simpleLogger.logFile", logFile)
			}
		}
		
		run_diagnostics()
		
		val scenarioFile = getString(scenarioOpt, cmd);
		var outDir = getOptString(outDirOpt, cmd, "output");
		
		val loader = new ModelStorage()
		
		
		val src = loader.loadCandidates(scenarioFile)
		
		val spaceGenerator = new CandidateSpaceGenerator()
		val hintsPath = getOptString(contractOpt, cmd, null);
		
		if (cmd.hasOption(genDefAdaptations.getOpt())){
			spaceGenerator.createDefaultCandidateSpace(src)
		} else if (hintsPath !== null) {
			val hints = loader.loadContractSet(hintsPath)
			spaceGenerator.createCandidateSpace(hints, src)
		}
		
		val optimizeAlgebraicLoops = cmd.hasOption(optLoops.opt)
		
		val runner = new AdaptiveCosimRunner(new OutputProcessor(outDir), new FmuLoader, false)
		val variantProcessor = if (optimizeAlgebraicLoops) {
			new AlgebraicLoopVariantProcessor(runner)
		} else {
			new VariantProcessor(runner)
		}
		val generator = new CandidatesGenerator(new ConstraintChecker,new VariantValidator, variantProcessor)
		
		generator.createVariantTree(src)
		generator.processAdaptations(src)
		
		val nVars = Integer.parseInt(cmd.getOptionValue(nVarsOpt.opt, Integer.MAX_VALUE.toString))
		
		generator.generateVariants(src, nVars, optimizeAlgebraicLoops)
		
		runner.close()
	}
	
	override start(IApplicationContext context) throws Exception {
		val args = context.getArguments().get("application.args") as String[]
		
		main(args)
		
		return IApplication.EXIT_OK
	}
	
	def private static String getString(Option opt, CommandLine cmd)
	{
		if (cmd.hasOption(opt.getOpt()))
		{
			return cmd.getOptionValue(opt.getOpt());
		} else
		{
			throw new IllegalArgumentException("Missing option --" + opt.getLongOpt())
		}
	}
	
	def private static String getOptString(Option opt, CommandLine cmd, String d)
	{
		if (cmd.hasOption(opt.getOpt()))
		{
			return cmd.getOptionValue(opt.getOpt())
		} else
		{
			return d
		}
	}
	
	def static run_diagnostics(){
		val ms = new ModelStorage()
		val f = new FileWriter("storage_test.xmi")
		f.write('''
		<?xml version="1.0" encoding="ASCII"?>
		<hintco:HintConfiguration xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:hintco="ua.ansymo.hintco">
		  <candidates identifier="c1"/>
		</hintco:HintConfiguration>
		''')
		f.close
		
		val candidates = ms.loadCandidates("storage_test.xmi")
		
		Assert.isTrue(candidates.eAllContents.filter(RootCandidateScenario).head.identifier == "c1")
		
		Factory.checkApi()
		
		Assert.isTrue(new File("storage_test.xmi").delete())
	}
	
	override stop() {}
	
}