package pl.edu.pw.mini.sg.movingtargets

import canes.game.{ Target, Depot, MovingTargetsGame }
import java.util.UUID
import org.scalatest.WordSpec

import collection.JavaConverters._
import org.scalatest.Matchers._
import pl.edu.pw.mini.jk.sg.game.immutable.Payoffs

class MovingTargetsImmutableGameSpec extends WordSpec {
  "GameWithTwoBases" when {
    val gameConfig = MovingTargetsGameConfig(MovingTargetsGame(UUID.fromString("123e4567-e89b-12d3-a456-426655440000"),
      List(Depot(1, 5, 5), Depot(2,20,20)),
      List(Target(1, 1, 2, List(2,33,50), 10, -5, -8, 4)),
      0.5, 2, 2, List(1,1), 1, 100, 1, 0), 1)

    val game = MovingTargetsImmutableGame(None, gameConfig,
      MovingTargetsDefenderState(Vector(DefenderPosition(0,0), DefenderPosition(0,0)), 0, gameConfig),
      MovingTargetsAttackerState.createState(false, gameConfig.targets.map(_.id)),
      0, 0)

    "not moved" should {

      "allow three attacker moves" in {
        game.attackerState.getPossibleMoves.asScala should contain theSameElementsAs List(WaitDecision, AttackDecision(1))
      }

      "allow 6² defender moves" in {
        game.defenderPositions.possibleMoves should have length(6*6)
      }

      "target should be in (5,5)" in {
        gameConfig.getTargetPosition(1,0) should equal((5.0,5.0))
      }

      "should not allow defender move to (15,15)" in {
        an[IllegalStateException] should be thrownBy game.makeMoves(MovingTargetsDefenderMove(Vector(DefenderPosition(10,10), DefenderPosition(0,0))), WaitDecision)
      }

      "should  not allow defender move to 5,8 5,8" in {
        an[IllegalStateException] should be thrownBy game.makeMoves(MovingTargetsDefenderMove(Vector(DefenderPosition(0,3), DefenderPosition(0,0))), WaitDecision)
      }

      "should  not allow defender move to 6,7" in {
        an[IllegalStateException] should be thrownBy game.makeMoves(MovingTargetsDefenderMove(Vector(DefenderPosition(1,2), DefenderPosition(0,0))), WaitDecision)
      }

      "time step shoul be 0" in {
        game.timeStep should be(0)
      }

    }

    "attacker attacked" should {
      val result = game.makeMoves(MovingTargetsDefenderMove(Vector(DefenderPosition(0,0), DefenderPosition(0,0))), AttackDecision(1))
      "not allow second attack" in {
        result._2.attackerState.getPossibleMoves.asScala shouldNot contain (AttackDecision(1))
      }

      "give reward 4 to defender" in {
        result._1.defenderPayoff should equal(4)
      }

      "give penalty -5 to attacker" in {
        result._1.attackerPayoff should equal(-5)
      }

      "game result should be 4 for defender" in {
        result._2.defenderScore should equal(4)
      }

      "game result should be -5 for attacker" in {
        result._2.attackerScore should equal(-5)
      }

      "time step shoul be 1" in {
        result._2.timeStep should be(1)
      }

      "next moves should give payoff 0" in {
        val p = result._2.makeMoves(MovingTargetsDefenderMove(Vector(DefenderPosition(0,0), DefenderPosition(0,0))), WaitDecision)._1

        p should equal(Payoffs(0,0,0,0))
      }
    }

    "attcker attacketd and defender left the target" should {
      val result = game.makeMoves(MovingTargetsDefenderMove(Vector(DefenderPosition(0,2), DefenderPosition(0,2))), AttackDecision(1))

      "give -8 penalty to defender" in {
        result._1.defenderPayoff should equal(-8)
      }

      "give 10 reward to attacker" in {
        result._1.attackerPayoff should equal(10)
      }

      "not allow second attack" in {
        result._2.attackerState.getPossibleMoves.asScala shouldNot contain (AttackDecision(1))
      }
    }

    "defender 1 moved to (0,0) and defender 2 moved to (0,2)" should {
      val result = game.makeMoves(MovingTargetsDefenderMove(Vector(DefenderPosition(0,0), DefenderPosition(0,2))), WaitDecision)

      "not allow defender 1 (0,4) move" in {
        assert(result._2.defenderPositions.possibleMoves.forall(_.newPositions(0) != DefenderPosition(0,4)))
      }

      "allow defender 2 (0,4) move" in {
        assert(result._2.defenderPositions.possibleMoves.exists(_.newPositions(1) == DefenderPosition(0,4)))
      }

    }
  }

  "game with 2-round attack" when {
    val gameConfig = MovingTargetsGameConfig(MovingTargetsGame(UUID.fromString("123e4567-e89b-12d3-a456-426655440000"),
      List(Depot(1, 5, 5), Depot(2,20,20)),
      List(Target(1, 1, 2, List(2,33,50), 10, -5, -8, 4)),
      0.5, 2, 2, List(1,1), 1, 100, 1, 2), 1)

    val game = MovingTargetsImmutableGame(None, gameConfig,
      MovingTargetsDefenderState(Vector(DefenderPosition(0,0), DefenderPosition(0,0)), 0, gameConfig),
      MovingTargetsAttackerState.createState(false, gameConfig.targets.map(_.id)),
      0, 0)

    val noProtMove = MovingTargetsDefenderMove(Vector(DefenderPosition(0,2), DefenderPosition(0,2)))
    val protMove = MovingTargetsDefenderMove(Vector(DefenderPosition(0,0), DefenderPosition(0,0)))

    val noProt1 = game.makeMoves(noProtMove, AttackDecision(1))
    val noProt2 = noProt1._2.makeMoves(noProtMove, WaitDecision)
    val noProt3 = noProt2._2.makeMoves(noProtMove, WaitDecision)

    val prot2 = noProt1._2.makeMoves(protMove, WaitDecision)
    val prot23= prot2._2.makeMoves(protMove, WaitDecision)

    "target is not protected" should {
      "not give payoffs in first round" in {noProt1._1 should equal(Payoffs(0,0,0,0))}
      "not give payoffs in second round" in {noProt2._1 should equal(Payoffs(0,0,0,0))}
      "give defender penalty in third round" in {noProt3._1 should equal(Payoffs(-8, 10, 1, 0))}
      "not give payoffs in fourth round" in { noProt3._2.makeMoves(noProtMove, WaitDecision)._1 should equal(Payoffs(0,0,0,0))}
      "not give payoffs in fourth round with protection" in { noProt3._2.makeMoves(protMove, WaitDecision)._1 should equal(Payoffs(0,0,0,0))}
    }

    "target is protected after one round" should {
      "give defender reward in second round" in {prot2._1 should equal(Payoffs(4,-5,0,1))}
      "not give defender reward in third round" in {prot23._1 should equal(Payoffs(0,0,0,0))}
    }

    "target is protected after two rounds" should {
      "give defender reward in third round" in {noProt2._2.makeMoves(protMove, WaitDecision)._1 should equal(Payoffs(4,-5,0,1))}
    }

  }
}
