package com.google.research.bleth.simulator;

import static com.google.common.truth.Truth.assertThat;

import com.google.appengine.api.datastore.dev.LocalDatastoreService;
import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.google.common.collect.Multimap;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class TracingSimulationIT {

    private final LocalServiceTestHelper helper =
            new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()
                    .setAutoIdAllocationPolicy(LocalDatastoreService.AutoIdAllocationPolicy.SCATTERED));

    private static final IMovementStrategy MOVE_UP = new UpMovementStrategy();
    private static final IMovementStrategy STATIONARY = new StationaryMovementStrategy();

    private static final int BOARD_DIMENSION_EQUALS_TWO = 2;
    private static final int MAX_ROUNDS_EQUALS_TWO = 2;
    private static final int NUMBER_OF_BEACONS_EQUALS_ONE = 1;
    private static final int NUMBER_OF_OBSERVERS_EQUALS_ONE = 1;
    private static final double TRANSMISSION_THRESHOLD_RADIUS_EQUALS_ONE = 1.0;
    private static final int AWAKENESS_CYCLE_EQUALS_TWO = 2;
    private static final int AWAKENESS_DURATION_EQUALS_ONE = 1;
    private static final AwakenessStrategyFactory.Type FIXES_AWAKENESS_STRATEGY_TYPE =
            AwakenessStrategyFactory.Type.FIXED;

    @Before
    public void setUp() {
        helper.setUp();
    }

    @Test
    public void runSimulationSingleRoundVerifyAgentsLocations() {
        // Create new simulation.
        AbstractSimulation simulation = new TracingSimulation.Builder()
                .setMaxNumberOfRounds(MAX_ROUNDS_EQUALS_TWO)
                .setRowNum(BOARD_DIMENSION_EQUALS_TWO)
                .setColNum(BOARD_DIMENSION_EQUALS_TWO)
                .setBeaconsNum(NUMBER_OF_BEACONS_EQUALS_ONE)
                .setObserversNum(NUMBER_OF_OBSERVERS_EQUALS_ONE)
                .setTransmissionThresholdRadius(TRANSMISSION_THRESHOLD_RADIUS_EQUALS_ONE)
                .setBeaconMovementStrategy(MOVE_UP)
                .setObserverMovementStrategy(STATIONARY)
                .setAwakenessCycle(AWAKENESS_CYCLE_EQUALS_TWO)
                .setAwakenessDuration(AWAKENESS_DURATION_EQUALS_ONE)
                .setAwakenessStrategyType(FIXES_AWAKENESS_STRATEGY_TYPE)
                .build();

        // Find agents and calculate their expected location after a single round.
        Location beaconInitialLocation = findAgent(simulation.getBoard(), "Beacon");
        Location observerInitialLocation = findAgent(simulation.getBoard(), "Observer");
        Location beaconExpectedLocation = predictLocationAfterMoveUp(beaconInitialLocation);
        Location observerExpectedLocation = observerInitialLocation;

        // Run single-rounded simulation and find agents again.
        simulation.run();
        Location beaconActualLocation = findAgent(simulation.getBoard(), "Beacon");
        Location observerActualLocation = findAgent(simulation.getBoard(), "Observer");

        assertThat(beaconActualLocation).isEqualTo(beaconExpectedLocation);
        assertThat(observerActualLocation).isEqualTo(observerExpectedLocation);
    }

    private Location findAgent(Board board, String type) {
        Multimap<Location, IAgent> agentsOnLocations = board.agentsOnBoard();
        for(int row = 0; row < board.getRowNum(); row++) {
            for(int col = 0; col < board.getColNum(); col++) {
                Location loc = Location.create(row, col);
                for(IAgent agent : agentsOnLocations.get(loc)) {
                    if (agent.getId() == 0 && agent.getType().equals(type)) {
                        return loc;
                    }
                }
            }
        }
        return null;
    }

    @After
    public void tearDown() {
        helper.tearDown();
    }

    private Location predictLocationAfterMoveUp(Location location) {
        if(location.row() == 0) return location;
        return Location.create(location.row() - 1, location.col());
    }
}

@@ -334,75 +334,8 @@
 er;%0A
-import com.google.common.collect.Multimap;%0Aimport org.junit.After;%0A
 impo
@@ -1467,28 +1467,16 @@
 Y_TYPE =
-%0A           
  Awakene
@@ -2605,34 +2605,24 @@
 lLocation = 
-findAgent(
 simulation.g
@@ -2620,36 +2620,43 @@
 ulation.
-getBoard(), %22Beacon%22
+beacons.get(0).getLocation(
 );%0A     
@@ -2689,34 +2689,24 @@
 lLocation = 
-findAgent(
 simulation.g
@@ -2704,38 +2704,45 @@
 ulation.
-getBoard(), %22Observer%22
+observers.get(0).getLocation(
 );%0A     
@@ -3025,34 +3025,24 @@
 lLocation = 
-findAgent(
 simulation.g
@@ -3044,28 +3044,35 @@
 ion.
-getBoard(), %22Beacon%22
+beacons.get(0).getLocation(
 );%0A 
@@ -3112,26 +3112,16 @@
 ation = 
-findAgent(
 simulati
@@ -3127,30 +3127,37 @@
 ion.
-getBoard(), %22Observer%22
+observers.get(0).getLocation(
 );%0A%0A
@@ -3344,443 +3344,87 @@
 ion 
-findAgent(Board board, String type) %7B%0A        Multimap%3CLocation, IAgent%3E agentsOnLocations = board.agentsOnBoard();%0A        for(int row = 0; row %3C board.getRowNum(); row++) %7B%0A            for(int col = 0; col %3C board.getColNum(); col++) %7B%0A                Location loc = Location.create(row, col);%0A                for(IAgent agent : agentsOnLocations.get(loc)) %7B%0A                    if (agent.getId() == 0 && agent.getType().equals(type)
+predictLocationAfterMoveUp(Location location) %7B%0A        if (location.row() == 0
 ) %7B%0A
@@ -3435,28 +3435,16 @@
         
-            
 return l
@@ -3449,293 +3449,24 @@
  loc
-;%0A                    %7D%0A                %7D%0A            %7D%0A        %7D%0A        return null;%0A    %7D%0A%0A    @After%0A    public void tearDown() %7B%0A        helper.tearDown();%0A    %7D%0A%0A    private Location predictLocationAfterMoveUp(Location location) %7B%0A        if(location.row() == 0) return location;
+ation;%0A        %7D
 %0A   

Remove findAgent.