package pl.edu.pw.mini.jk.sg.game.graph.advanceable;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.apache.commons.collections4.Factory;
import org.junit.Before;
import org.junit.Test;

import com.google.common.base.Stopwatch;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableList;

import pl.edu.pw.mini.jk.sg.game.DefenderAttackerGame;
import pl.edu.pw.mini.jk.sg.game.common.DefenderMove;
import pl.edu.pw.mini.jk.sg.game.configuration.StaticGameConfigurationReader;
import pl.edu.pw.mini.jk.sg.game.graph.moves.many.advanceable.ManyMovesAdvanceableStateGame;
import pl.edu.pw.mini.jk.sg.game.graph.moves.many.advanceable.SingleUnitDestination;
import pl.edu.pw.mini.jk.sg.game.graph.moves.single.DesiredConfigurationMove;

public class SimulationSpeedTest {

    private AdvanceableGraphGameConfiguration game1;

    private static final int[][] defenderMoveSequences = { { 0, 6, 6, 6, 6 }, { 6, 6, 6, 5, 6 }, { 6, 6, 6, 6, 4 },
            { 6, 6, 6, 6, 6 } };

    private static final int[][] attackerMoveSequences = { { 5, 5, 5, 5, 5 }, { 5, 5, 5, 5, 6 } };

    private static final int stepLimit = 5;
    private static final int gamesPerTest = 100000;

    @Before
    public void setUp() throws Exception {
        game1 = StaticGameConfigurationReader
                .readFromStream(Thread.currentThread().getContextClassLoader().getResourceAsStream("game1.json"));
    }

    public static ImmutableList<DesiredConfigurationMove> arrayToDcmList(int[] destinations) {
        return ImmutableList.copyOf(
                Arrays.stream(destinations).mapToObj(i -> new DesiredConfigurationMove(new int[] { i })).iterator());
    }

    public static ImmutableList<SingleUnitDestination> arrayToSudList(int[] destinations) {
        return ImmutableList.copyOf(Arrays.stream(destinations).mapToObj(SingleUnitDestination::new).iterator());
    }

    public static <AM, DM extends DefenderMove, G extends DefenderAttackerGame<AM, DM, ?, ?, ?>> void playeSingleGame(
            G game, List<DM> dm, List<AM> am, int rounds) {

        for (int i = 0; i < rounds; i++) {
            game.getCurrentState();
            game.playDefenderMove(dm.get(i));
            game.playAttackerMove(am.get(i));
            game.finishRound();
        }
    }

    @Test
    public void test() {
        final Cache<Object, ImmutableList<DesiredConfigurationMove>> cache1 = CacheBuilder.newBuilder().build();
        final Factory<AdvanceableStateGraphGame> smGameFactory = () -> new AdvanceableStateGraphGame(game1, cache1, false);

        final Cache<Integer, ImmutableList<SingleUnitDestination>> cache2 = CacheBuilder.newBuilder().build();
        final Factory<ManyMovesAdvanceableStateGame> mmGameFactory = () -> new ManyMovesAdvanceableStateGame(game1,
                Optional.of(cache2), false) ;

        final List<List<DesiredConfigurationMove>> defenderDcm = Arrays.stream(defenderMoveSequences)
                .map(SimulationSpeedTest::arrayToDcmList).collect(Collectors.toList());

        final List<List<DesiredConfigurationMove>> attackerDcm = Arrays.stream(attackerMoveSequences)
                .map(SimulationSpeedTest::arrayToDcmList).collect(Collectors.toList());

        final List<List<SingleUnitDestination>> defenderSud = Arrays.stream(defenderMoveSequences)
                .map(SimulationSpeedTest::arrayToSudList).collect(Collectors.toList());

        final List<List<SingleUnitDestination>> attackerSud = Arrays.stream(attackerMoveSequences)
                .map(SimulationSpeedTest::arrayToSudList).collect(Collectors.toList());
        
        Stopwatch sw1 = Stopwatch.createStarted();
        for (int i = 0; i < gamesPerTest; i++) {
            playeSingleGame(smGameFactory.create(), defenderDcm.get(i % defenderDcm.size()),
                    attackerDcm.get(i % attackerDcm.size()), stepLimit);
        }
        sw1.stop();

        Stopwatch sw2 = Stopwatch.createStarted();
        for (int i = 0; i < gamesPerTest; i++) {
            playeSingleGame(mmGameFactory.create(), defenderSud.get(i % defenderSud.size()),
                    attackerSud.get(i % attackerSud.size()), stepLimit);
        }
        sw2.stop();

        final long t1 = sw1.elapsed(TimeUnit.MILLISECONDS);
        final long t2 = sw2.elapsed(TimeUnit.MILLISECONDS);
        
//        TestCase.assertTrue(t1 > 1000);
//        TestCase.assertTrue(t2 > 1000);
        System.err.format("%d %d\n", t1, t2);
//        TestCase.assertTrue((double)t1/(double)t2 < 1.1);
//        TestCase.assertTrue((double)t2/(double)t1 < 1.1);
        
    }

}
