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

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import org.apache.commons.math3.random.RandomGenerator;

import com.google.common.base.Stopwatch;

import pl.edu.pw.mini.jk.sg.game.DefenderAttackerGame;
import pl.edu.pw.mini.jk.sg.game.GameVariantRunner;
import pl.edu.pw.mini.jk.sg.game.NormalizableFactory;
import pl.edu.pw.mini.jk.sg.game.NormalizableFactoryAction;
import pl.edu.pw.mini.jk.sg.game.common.Cloneable;
import pl.edu.pw.mini.jk.sg.game.common.DefenderMove;
import pl.edu.pw.mini.jk.sg.game.common.SecurityGame;
import pl.edu.pw.mini.jk.sg.game.common.advanceable.AdvanceableAttackerState;
import pl.edu.pw.mini.jk.sg.game.common.advanceable.AdvanceableDefenderState;
import pl.edu.pw.mini.jk.sg.gteval.result.TimedTrainingResult;
import pl.edu.pw.mini.jk.sg.gteval.result.TimedTrainingResultFactory;
import pl.edu.pw.mini.jk.sg.opt.util.GameUtil;
import pl.edu.pw.mini.jk.sg.game.GameVariantRunnerResult;

public interface GameVariantMethodConfig extends MethodConfig {
    pl.edu.pw.mini.jk.sg.game.GameVariant getGameVariant();
    <DM extends  DefenderMove, AM, AS extends AdvanceableAttackerState<AM>, DS extends AdvanceableDefenderState<DM>, G extends Cloneable<G> & SecurityGame & DefenderAttackerGame<AM, DM, AS, DS, ?>> TimedTrainingResultFactory<DM,AM> performTrainingWithGame(GameMatrices<AM, DM, AS, DS, G> gameMatrices, RandomGenerator rng);

default public <DM extends DefenderMove, DS extends AdvanceableDefenderState<DM>, AM, AS extends AdvanceableAttackerState<AM>, G extends pl.edu.pw.mini.jk.sg.game.common.Cloneable<G> & SecurityGame & DefenderAttackerGame<AM, DM, AS, DS, ?>>
	    TimedTrainingResult<DM,AM> performExperiment(GameMatrices<AM, DM, AS, DS, G> gameMatrices, final RandomGenerator rng) {
	final Stopwatch uctWatch = Stopwatch.createStarted();
	final TimedTrainingResultFactory<DM,AM> trf = performTrainingWithGame(gameMatrices, rng);
	uctWatch.stop();
	return trf.create(uctWatch.elapsed(TimeUnit.MILLISECONDS));
    }


default public GameVariantRunnerResult<TimedTrainingResult<?,?>> performTraining(final GameVariantRunner runner , RandomGenerator rng) throws IOException {
    return getGameVariant().runWithFactory(runner,
						new NormalizableFactoryAction<TimedTrainingResult<?,?>>() {
						    @Override 
						    public <AM, DM extends  DefenderMove,
						     AS extends AdvanceableAttackerState<AM>,
                                                     DS extends AdvanceableDefenderState<DM>,
                                                     G extends Cloneable<G> & SecurityGame &
                                                     DefenderAttackerGame<AM, DM, AS, DS, ?>>
							TimedTrainingResult<?,?> runWithNormalizableFactory(NormalizableFactory<AM, DM, G> factory, int roundLimit){
							return performExperiment(GameUtil.calculateGameMatrices(factory, roundLimit), rng);
						    }
						});
}
}

