package pl.edu.pw.mini.jk.sg.gteval.config;

import argonaut.Json;
import com.google.common.cache.CacheBuilder;
import java.io.File;
import java.io.IOException;
import java.util.Locale;

import com.lexicalscope.jewel.cli.CliFactory;
import java.util.Optional;
import pl.edu.pw.mini.jk.sg.game.GameVariantRunner;

import pl.edu.pw.mini.jk.sg.game.configuration.StaticGameConfigurationReader;
import pl.edu.pw.mini.jk.sg.game.graph.GraphGameVariantRunner;
import pl.edu.pw.mini.jk.sg.game.graph.advanceable.AdvanceableGraphGameConfiguration;
import pl.edu.pw.mini.jk.sg.gteval.MethodConfig;
import pl.edu.pw.mini.jk.sg.gteval.MethodConfigDecoder;
import pl.edu.pw.mini.jk.sg.game.graph.NormalizableAdvanceableStateGraphGameFactory;
import pl.edu.pw.mini.jk.sg.game.graph.NormalizableManyMovesAdvanceableStateGameFactory;
import pl.edu.pw.mini.jk.sg.gteval.RawCmdOptions;
import pl.edu.pw.mini.sg.movingtargets.MovingTargetsGameConfig;
import pl.edu.pw.mini.sg.movingtargets.MovingTargetsGameConfigReader;
import pl.edu.pw.mini.sg.movingtargets.MovingTargetsVariantRunner;
import pl.edu.pw.mini.sg.movingtargets.coord.CoordNormalizableFactory;
import pl.edu.pw.mini.sg.movingtargets.multi.MovingTargetsMultiMutableGameFactory;
import pl.edu.pw.mini.sg.movingtargets.mutable.MovingTargetsGameFactory;
import pl.edu.pw.mini.jk.sg.mixeduct.TreeLogConfig;
import pl.edu.pw.mini.jk.sg.mixeduct.TreeLogConfigEnable;
import scala.Some;
import scala.Tuple2;

public class GtevalOptions {
    private final PluginLibrary gamePlugins = PluginLibrary.defaultLibrary();
    private final Optional<File> jsonStrategyOutput;
    private final File stackelbergSolutionPath;
    private final MethodConfig methodConfig;
    private final Json unparsedMethodConfig;
    private final GameVariantRunner gvr;
    
	public GtevalOptions(AdvanceableGraphGameConfiguration sgc, File stackelbergSolutionPath, int roundLimit,
			     MethodConfig methodConfig, final Json unparsedMethodConfig, Optional<File> jsonStrategyOutput) {
		super();
		this.stackelbergSolutionPath = stackelbergSolutionPath;
		this.methodConfig = methodConfig;
		this.jsonStrategyOutput = jsonStrategyOutput;
		this.unparsedMethodConfig = unparsedMethodConfig;
		this.gvr = new GraphGameVariantRunner(sgc, roundLimit);
	}

    public GtevalOptions(File gameConfigFile, File stackelbergSolutionPath,
			 MethodConfig methodConfig, final Json unparsedMethodConfig, Optional<File> jsonStrategyOutput) {
	super();
	this.stackelbergSolutionPath = stackelbergSolutionPath;
	this.methodConfig = methodConfig;
	this.jsonStrategyOutput = jsonStrategyOutput;
	this.unparsedMethodConfig = unparsedMethodConfig;
	this.gvr = gamePlugins.getGameVariantRunner(gameConfigFile);
    }

    public GtevalOptions(File gameConfigFile, File gameTransformer, File stackelbergSolutionPath,
			 MethodConfig methodConfig, final Json unparsedMethodConfig, Optional<File> jsonStrategyOutput) {
	super();
	this.stackelbergSolutionPath = stackelbergSolutionPath;
	this.methodConfig = methodConfig;
	this.jsonStrategyOutput = jsonStrategyOutput;
	this.unparsedMethodConfig = unparsedMethodConfig;
	this.gvr = gamePlugins.getGameVariantRunner(gameConfigFile, gameTransformer);
    }

    
    public Optional<File> getJsonStrategyOutput() {
	return jsonStrategyOutput;
    }

    public Json getUnparsedMethodConfig() {
	return unparsedMethodConfig;
    }

    public GameVariantRunner getGameVariantRunner() {
	return gvr;
    }
    
    public File getStackelbergSolutionPath() {
	return stackelbergSolutionPath;
    }

    
    public MethodConfig getMethodConfig() {
	return methodConfig;
    }

	public static final GtevalOptions parseCommandlineOptions(final String[] args)
			throws IOException {
		RawCmdOptions rawOptions = CliFactory.createCli(RawCmdOptions.class).parseArguments(args);

		final Optional<File> jSO;

		if(rawOptions.isJsonStrategyOutput())
		    jSO = Optional.of(rawOptions.getJsonStrategyOutput());
		else
		    jSO = Optional.empty();
		
		Locale.setDefault(Locale.US);

		final Tuple2<MethodConfig, Json> mc;

		if(rawOptions.isTreeLogDir()) {
		    final TreeLogConfig tlc = new TreeLogConfigEnable(rawOptions.getTreeLogFreq(),
								      rawOptions.getTreeLogDir()
                    );
		    mc = MethodConfigDecoder.fromFile(rawOptions.getMixedStrategyConfig(), tlc);
		} else {
		    mc = MethodConfigDecoder.fromFile(rawOptions.getMixedStrategyConfig());
		}
		    
		if(rawOptions.isSgcPath()) {
		    return new GtevalOptions(StaticGameConfigurationReader.readAagcFromFile(rawOptions.getSgcPath()),
					     rawOptions.getStackelbergSolutionPath(), rawOptions.getRoundLimit(), mc._1, mc._2				
					     , jSO);
		} else if(rawOptions.isGameConfig()) {
		    if(rawOptions.isGameTransformer()) {
			return new GtevalOptions(rawOptions.getGameConfig(), rawOptions.getGameTransformer(), rawOptions.getStackelbergSolutionPath(),
					     mc._1, mc._2, jSO);
		    } else {
			return new GtevalOptions(rawOptions.getGameConfig(), rawOptions.getStackelbergSolutionPath(),
					     mc._1, mc._2, jSO);
		    }
		} else {
		    throw new IllegalArgumentException("No game path specified");
		}
	}
}
