package pl.edu.pw.mini.jk.sg.opt.util;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;

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.game.common.advanceable.AdvanceableState;
import pl.edu.pw.mini.jk.sg.gteval.GameMatrices;

public final class GameUtil {

    public static <DM extends DefenderMove, AM> GamePayoff getPayoff(final List<DM> defenderSequence,
								     final List<AM> attackerSequence, final NormalizableFactory<AM, DM, ? extends DefenderAttackerGame<AM, DM, ?, ?, ?>> gameFactory) {
        Preconditions.checkArgument(defenderSequence.size() == attackerSequence.size());

        DefenderAttackerGame<AM, DM, ?, ?, ?> game = gameFactory.create();
        for (int i = 0; i < defenderSequence.size() && ! game.accidentOccured(); i++) {
            game.playDefenderMove(defenderSequence.get(i));
            game.playAttackerMove(attackerSequence.get(i));
            game.finishRound();
        }

        return new GamePayoff(game.getDefenderResult(), game.getAttackerResult());
    }

    
    public static <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, ?>> GameMatrices<AM, DM, AS, DS, G> calculateGameMatrices(
																																			   NormalizableFactory<AM, DM, G> noaiGameFactory, int roundLimit) {
		DefenderAttackerGame<AM, DM, AS, DS, ?> game = noaiGameFactory.create();
		final List<ImmutableList<AM>> attackerSequences = GameUtil
				.getAllPossiblePlayerMoveSequences(game.getAttackerInitialState(), roundLimit)
				.collect(Collectors.toList());

		final Lazy<List<ImmutableList<DM>>> defenderSequences = new Lazy<>( () -> GameUtil
				.getAllPossiblePlayerMoveSequences(game.getDefenderInitialState(), roundLimit)
										    .collect(Collectors.toList()));

		return new GameMatrices<>(attackerSequences, defenderSequences, roundLimit, noaiGameFactory);
	}


    private static int v = 0;
    public static <M> Stream<ImmutableList<M>> getAllPossiblePlayerMoveSequences(final AdvanceableState<M> state,
            int desiredTimeStep) {
	
        if (desiredTimeStep == state.getTimeStep()) {
	    if(v%10000 == 0) System.out.println(v);
	        v++;
            return Stream.of(ImmutableList.<M> of());
	}
        else {
            return state.getPossibleMoves().stream().flatMap(getAllMovesForState(state, desiredTimeStep));
	}

    }

    private static <M> Function<? super M, ? extends Stream<? extends ImmutableList<M>>> getAllMovesForState(
            final AdvanceableState<M> state, final int desiredTimeStep) {
        return move -> {
            final AdvanceableState<M> next = state.advance(move);
            return getAllPossiblePlayerMoveSequences(next, desiredTimeStep)
                    .map(stream -> ImmutableList.<M> builder().add(move).addAll(stream).build());
        };
    }

}
