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

import java.util.List;
import java.util.function.Function;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;

import pl.edu.pw.mini.jk.lib.Lazy;
import pl.edu.pw.mini.jk.sg.game.DefenderAttackerGame;
import pl.edu.pw.mini.jk.sg.game.NormalizableFactory;
import pl.edu.pw.mini.jk.sg.game.common.DefenderMove;
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.mixeduct.attackerresponse.ExpectedPayoff;
import pl.edu.pw.mini.jk.sg.opt.util.GamePayoff;
import pl.edu.pw.mini.jk.sg.opt.util.GameUtil;

public class GameMatrices<AM, DM extends DefenderMove, AS extends AdvanceableAttackerState<AM>, DS extends AdvanceableDefenderState<DM>, G extends pl.edu.pw.mini.jk.sg.game.common.Cloneable<G> & DefenderAttackerGame<AM, DM, AS, DS, ?>> {
    private final NormalizableFactory<AM, DM, G> noaiFactory;
	private final List<ImmutableList<AM>> attackerSequences;
	private final Lazy<List<ImmutableList<DM>>> defenderSequences;
	private final int gameLength;

	public GameMatrices(List<ImmutableList<AM>> attackerSequences, Lazy<List<ImmutableList<DM>>> defenderSequences,
			    int gameLength, NormalizableFactory<AM, DM, G> noaiFactory) {
		super();
		this.attackerSequences = attackerSequences;
		this.defenderSequences = defenderSequences;
		this.noaiFactory = noaiFactory;
		this.gameLength = gameLength;
	}

    public GameMatrices<AM, DM, AS, DS, G> denormalize() {
	return new GameMatrices<>(attackerSequences, defenderSequences, gameLength, noaiFactory.denormalize());
    }
    
    public List<ImmutableList<AM>> getAttackerSequences() {
		return attackerSequences;
	}

	public List<ImmutableList<DM>> getDefenderSequences() {
	    return defenderSequences.get();
	}

    public NormalizableFactory<AM, DM, G> getNoaiFactory() {
		return noaiFactory;
	}
	
	public int getGameLength() {
		return gameLength;
	}

    public DS getDefenderInitialState() {
	return getNoaiFactory().create().getDefenderInitialState();
    }
    public AS getAttackerInitialState() {
	return getNoaiFactory().create().getAttackerInitialState();
    }

	public GamePayoff getGamePayoff(ImmutableList<DM> defenderSequence, ImmutableList<AM> attackerSequence) {
		final Function<PlayScenario<AM, DM>, GamePayoff> getResultFunc = ps -> GameUtil
				.getPayoff(ps.defenderSequence(), ps.attackerSequence(), noaiFactory);
		return getResultFunc.apply(new PlayScenario<>(defenderSequence, attackerSequence));
		
	}

    public ExpectedPayoff getExpectedPayoff(ImmutableMap<ImmutableList<DM>, Double> defenderStrategy,
					    ImmutableMap<ImmutableList<AM>, Double> attackerStrategy) {
	final double dsum = defenderStrategy.values().stream().mapToDouble(i -> i).sum();
	final double asum = attackerStrategy.values().stream().mapToDouble(i -> i).sum();
	final ExpectedPayoff exp = ExpectedPayoff.sum(attackerStrategy.entrySet().stream()
						.map(ae -> ExpectedPayoff.sum(defenderStrategy.entrySet().stream()
									      .map(de -> 
				     new ExpectedPayoff(
							getGamePayoff(de.getKey(), ae.getKey()),
							(de.getValue()/dsum) * (ae.getValue()/asum)
							)
				     )
									      )));
	
	return new ExpectedPayoff(exp.attackerPayoff(), exp.defenderPayoff());
    }
}
