package pl.edu.pw.mini.game.search

import pl.edu.pw.mini.sg.rng.RandomGenerator
import pl.edu.pw.mini.sg.game.deterministic.AdvanceablePlayerVisibleState
import pl.edu.pw.mini.sg.util.CachedHashPair

import scala.runtime.ScalaRunTime

/**
  * Represents the current state of the attacker (his information set)
  * @param gameConfig         configuration of the game the attacker is playing
  * @param attackerPosition   current attacker position
  * @param timeStep           current timestep
  */
final case class SearchGameAttackerState(
  gameConfig: SearchGameConfig,
  attackerPosition: Int,
  timeStep: Int

) extends AdvanceablePlayerVisibleState[SearchGameAttackerMove, SearchGameAttackerState] {
  override lazy val hashCode : Int = ScalaRunTime._hashCode(this)

  override def canEqual(that: Any): Boolean = that match {
    case _: SearchGameAttackerState => true
    case _ => false
  }

  override def equals(o: scala.Any): Boolean = o match {
    case sgas: SearchGameAttackerState =>
      sgas.attackerPosition == attackerPosition && sgas.timeStep == timeStep
    case _ => false
  }

  override def toString: String = s"SGAS($attackerPosition, $timeStep)"

  def continuation(move: SearchGameAttackerMove): SearchGameAttackerState = {
    require(timeStep < gameConfig.rounds)
    //Maybe: require move to be in possible moves
    require(possibleMoves.contains(move))
    gameConfig.attackerCache.get(CachedHashPair(move, this), (p: CachedHashPair[SearchGameAttackerMove, SearchGameAttackerState]) => {
      val m = p.first
      val s = p.second
        s.copy(attackerPosition = m.newPosition, timeStep = timeStep + 1)
    })
  }

  override def possibleContinuations(move: SearchGameAttackerMove): List[SearchGameAttackerState] = List(continuation(move))

  override def sampleContinuations(move: SearchGameAttackerMove)(implicit rng: RandomGenerator): SearchGameAttackerState = continuation(move)

  override def possibleMoves: List[SearchGameAttackerMove] = {
    if(timeStep >= gameConfig.rounds)
      Nil
    else {
        gameConfig.possibleAttackerMoves(attackerPosition)
    }
  }
}
