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

import pl.edu.pw.mini.sg.game.deterministic.PlayerVisibleState
import pl.edu.pw.mini.sg.util.CachedHashPair
import pl.edu.pw.mini.sg.util.TraversableUtils.combs

import scala.runtime.ScalaRunTime

/**
  * Class for the defender state in the search game (his information set)
  * @param gameConfig     configuration of the game the defender is playing
  * @param unitPositions  positions of all defender units
  * @param timeStep       current time step
  * @param tracks         currently visible tracks (tracks in vertices where there are defender units)
  */
final case class SearchGameDefenderState(
  gameConfig: SearchGameConfig,
  unitPositions: List[Int],
  timeStep: Int,
  tracks: Set[Int]
) extends PlayerVisibleState[List[Int]] {
  override lazy val hashCode : Int = ScalaRunTime._hashCode(this)

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

  override def equals(o: scala.Any): Boolean = o match {
    case sgds: SearchGameDefenderState => sgds.unitPositions == unitPositions && sgds.timeStep == timeStep && (sgds.tracks == tracks)
    case _ => false
  }

  override def toString: String = s"SGDS($unitPositions,$timeStep,$tracks)"

  def continuation(move: List[Int], newTracks: Set[Int]): SearchGameDefenderState = {
    require(timeStep < gameConfig.rounds)
    //Maybe: require newPosition to be in defenderVertices
    //Maybe: require move to be in possible moves
    gameConfig.defenderCache.get(CachedHashPair((move, newTracks), this), (p: CachedHashPair[(List[Int], Set[Int]), SearchGameDefenderState]) => {
      val (m, t) = p.first
      val s = p.second
      s.copy(unitPositions = m, timeStep = timeStep + 1, tracks = t)
    })
  }

  override def possibleMoves: List[List[Int]] = {
    if(timeStep >= gameConfig.rounds)
      Nil
    else {
      if(timeStep == gameConfig.rounds - 1)
        unitPositions.map(List(_))
      else
        gameConfig.defenderMoveCache.get(unitPositions, (p: List[Int]) => {
          combs(p.zipWithIndex.map{case (position, defender) => gameConfig.possibleDefenderMoves(defender)(position)}).map(_.toList).toList
        })
    }
  }
}
