package pl.edu.pw.mini.jk.sg.mixeduct.transfer;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Stream;

import org.apache.commons.collections4.queue.CircularFifoQueue;
import org.apache.commons.math3.random.RandomGenerator;

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.advanceable.AdvanceableDefenderState;
import pl.edu.pw.mini.jk.sg.mixeduct.util.TreeUtils;
import pl.edu.pw.mini.jk.sg.uct.tree.UctNode;

public class BestMoveFrequencyProbability<DM extends DefenderMove, DS extends AdvanceableDefenderState<DM>>
        implements AttackersInformationTransfer<DM,DS> {
    private final CircularFifoQueue<ImmutableList<DM>> bestHistory;
    private final int roundLimit;

    public BestMoveFrequencyProbability(final int roundLimit, final int historyLength) {
        super();
        bestHistory = new CircularFifoQueue<>(historyLength);
        this.roundLimit = roundLimit;
    }

    @Override
    public UctNode<DS, DM> newAttackerTree(final UctNode<DS, DM> oldTree, final RandomGenerator rng) {
        ImmutableList<DM> bestMove0 = TreeUtils.getBestPathInTreeExtendedJava(oldTree, roundLimit, rng);
        bestHistory.add(bestMove0);
        return oldTree.createEmptyTree();
    }

    static <R,T> R fold(Stream<T> s, R initial, BiFunction<R, T, R> combine) {
        R ret = initial;
        Iterator<T> it = s.iterator();
        while(it.hasNext()){
            ret=combine.apply(ret, it.next());
        }
        return ret;
    }

    @Override
    public Map<ImmutableList<DM>, Double> getDefenderMoveProbabilities(final RandomGenerator rng, java.util.function.Consumer<? super UctNode<? extends DS,? extends DM>> tlc) {
        final HashMap<ImmutableList<DM>, Double> output = new HashMap<>();

        for (final ImmutableList<DM> move : bestHistory) {
            output.put(move, output.getOrDefault(move, 0.) + 1);
        }

        return output;
    }

}
