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

import argonaut.Json;
import java.util.Comparator;
import java.util.Map;
import java.util.stream.Collectors;

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

import pl.edu.pw.mini.jk.lib.PmfUtils;
import pl.edu.pw.mini.jk.sg.game.common.DefenderMove;
import pl.edu.pw.mini.jk.sg.gteval.GameMatrices;
import pl.edu.pw.mini.jk.sg.gteval.PayoffUtils;
import pl.edu.pw.mini.jk.sg.gteval.Side;
import squants.time.Time;


public class TrainingResult<DM extends DefenderMove, AM> {
    private final ImmutableMap<ImmutableList<DM>, Double> defenderStrategy;
    private final ImmutableMap<ImmutableList<AM>, Double> attackerStrategy;
    private final GameMatrices<AM, DM, ?, ?, ?> gameMatrices; //TODO usuniecie tej zależności
    
    public TrainingResult(final Map<ImmutableList<DM>, Double> defenderStrategy, final Map<ImmutableList<AM>, Double> attackerStrategy,
			  final GameMatrices<AM,DM,?,?,?> gameMatrices) {
	this.defenderStrategy = PmfUtils.normalizeStrategy(defenderStrategy);
	this.attackerStrategy = PmfUtils.normalizeStrategy(attackerStrategy);
	this.gameMatrices = gameMatrices.denormalize();
    }

    public ImmutableMap<ImmutableList<DM>, Double> getDefenderStrategy() {
	return defenderStrategy;
    }

    public ImmutableMap<ImmutableList<AM>, Double> getAttackerStrategy() {
	return attackerStrategy;
    }
    
    public String methodSpecificStatistics() {
	return "";
    }

    public Json getTrainingResultAsJson(final String methodName, final Json methodConfig, final Time preprocessingTime) {
	return getGameMatrices().getNoaiFactory().defenderResultAsJson(getDefenderStrategy(), 0, methodName, methodConfig, preprocessingTime);
    }

    
    public String methodStatistics() {
	StringBuilder out = new StringBuilder();
	out.append("Attacker strategy:\n");
	out.append("The payoff: " + PayoffUtils.getExpectedPayoff(gameMatrices, attackerStrategy, defenderStrategy, Side.ATTACKER)).append("\n");
	for (Map.Entry<ImmutableList<AM>, Double> as : attackerStrategy.entrySet()) {
	    out.append(String.format("M: %s, Pr: %f\n", as.getKey(), as.getValue()));
	}
	out.append("Defender strategy: \n");
	out.append("The payoff: " + PayoffUtils.getExpectedPayoff(gameMatrices, attackerStrategy, defenderStrategy, Side.DEFENDER)).append("\n");;
	for (Map.Entry<ImmutableList<DM>, Double> pf : getNonzeroMovesPayoff(gameMatrices, attackerStrategy, defenderStrategy).entrySet()
		 .stream().sorted(Comparator
				  .<Map.Entry<ImmutableList<DM>, Double>, Double>comparing(e -> defenderStrategy.get(e.getKey())).reversed())
		 .collect(Collectors.toList())) {
	    out.append(String.format("M: %s, Pr: %f  Pa: %f\n", pf.getKey(), defenderStrategy.get(pf.getKey()), pf.getValue()));
	}
	out.append(String.format("PSUM: %f\n", defenderStrategy.values().stream().mapToDouble(i -> i).sum()));
	out.append(methodSpecificStatistics());
	return out.toString();
    }

    
    private static <DM extends DefenderMove, AM> Map<ImmutableList<DM>, Double> getNonzeroMovesPayoff(
												      GameMatrices<AM, DM, ?, ?, ?> gameMatrices, final Map<ImmutableList<AM>, Double> amp,
												      final Map<ImmutableList<DM>, Double> dmp) {
	return dmp.entrySet().stream().filter(e -> e.getValue() > 0)
	    .collect(Collectors.toMap(e -> e.getKey(), e -> getExpectedPayoff(gameMatrices, amp, e.getKey())));
    }
    
    private static <DM extends DefenderMove, AM> double getExpectedPayoff(GameMatrices<AM, DM, ?, ?, ?> gameMatrices,
									  Map<ImmutableList<AM>, Double> amp, ImmutableList<DM> defenderMove) {
	return amp.entrySet().stream()
	    .mapToDouble(
			 e -> e.getValue() * gameMatrices.getGamePayoff(defenderMove, e.getKey()).getDefenderPayoff())
	    .sum();
    }

    public GameMatrices<AM, DM, ?, ?, ?> getGameMatrices() { return gameMatrices; }
    
}
