package pl.edu.pw.mini.jk.sg.mixeduct.attackerresponse.uct;

import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.math3.distribution.EnumeratedDistribution;
import org.apache.commons.math3.random.RandomGenerator;
import org.apache.commons.math3.util.Pair;

import com.google.common.collect.ImmutableList;

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.GameMatrices;
import pl.edu.pw.mini.jk.sg.mixeduct.attackerresponse.AttackerSolver;
import pl.edu.pw.mini.jk.sg.mixeduct.attackerresponse.AttackerSolverResult;
import pl.edu.pw.mini.jk.sg.uct.I2UctConfig;
import pl.edu.pw.mini.jk.sg.uct.UctPlayer;
import pl.edu.pw.mini.jk.sg.uct.tree.UctNode;
import pl.edu.pw.mini.jk.sg.uct.tree.UctTreeFactory;


public class UctAttacker implements AttackerSolver {
    private final I2UctConfig<SecurityGame> config;
    private final TreeToStrategy treeToStrategy;

    public UctAttacker(final I2UctConfig<SecurityGame> config,
		       final TreeToStrategy treeToStrategy) {
		super();
		this.config = config;
		this.treeToStrategy = treeToStrategy;
	}

	@Override
    public <AM, AS extends AdvanceableAttackerState<AM>, DM extends DefenderMove, DS extends AdvanceableDefenderState<DM>> AttackerSolverResult<AM> calculateAttackerStrategy(
            Map<ImmutableList<DM>, Double> defenderMoveProbabilities, GameMatrices<AM, DM, AS, DS, ?> gameMatrices,
            RandomGenerator rng) {
        final EnumeratedDistribution<ImmutableList<DM>> defenderStrategy = new EnumeratedDistribution<>(rng,
                defenderMoveProbabilities.entrySet().stream()
                        .map(e -> new Pair<ImmutableList<DM>, Double>(e.getKey(), e.getValue()))
                        .collect(Collectors.toList()));
        final AttackerSecurityGameViewFactory<AM, AS, DM> factory = new AttackerSecurityGameViewFactory<AM, AS, DM>(
                gameMatrices.getNoaiFactory(), defenderStrategy);

	final UctNode<AS, AM> tree = config.uctTreeFactory().<AS, AM>create(gameMatrices.getAttackerInitialState());
        final UctPlayer<AM, AS, AttackerSecurityGameView<AM, AS, DM>> player = UctPlayer.fromConfig(factory, gameMatrices.getGameLength(), config,  rng);
	for(int i=0; i < gameMatrices.getGameLength(); i++) {
	    player.doLearningSequence(i, tree);
	}
        //TODO: tu trzeba wykonać zestaw operacji:
        // - pobrać wszystkie dobre sekwencje z drzewa
        // - wybrać te maksymalizujące wynik obrońcy
        // - zwrócić je (kilka czy jedną)?
        // OPCJA 2
        // - w drzewie UCT trzymać obie wypłaty i robić jakiś rodzaj tie-breakingu
        // ta opcja ma pewien plus: będziemy dokładniej sprawdzać te dobre ruchy. 
        
	return AttackerSolverResult.fromPairs(treeToStrategy.treeToStrategy(tree, gameMatrices, rng), false);
    }

	@Override
	public boolean isExact() {
		return false;
	}

}
