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

import java.util.UUID

import com.github.benmanes.caffeine.cache.{Cache, Caffeine}
import pl.edu.pw.mini.sg.game.deterministic.plugin.config.GameConfig
import pl.edu.pw.mini.sg.game.graph.GraphConfig
import pl.edu.pw.mini.sg.util.CachedHashPair

import scala.runtime.ScalaRunTime

/***
  * Configuration class for search game. Contains information about the game, a list of possible moves for
  * each defender and for the attacker and also a move cache.
  * @param graphConfig                The directed game graph
  * @param rounds                     The number of timesteps the game lasts
  * @param defenderUnitCount          Number of units the defender controls
  * @param attackerStartingPosition   Vertex where tha attacker starts
  * @param defenderStartingPositions  Starting vertex for each defender
  * @param targets                    List of vertices that are targets for the attacker with their payoffs (for the attacker)
  * @param defenderVertices           Defines a list of vertices the unit can move to for each defender
  * @param trackVertices              A list of vertices where the attacker leaves tracks
  * @param coveringUpTracksPossible   Flag that tells if covering up tracks is possible for the attacker. When covering up tracks, the attacker stays in the same vertex.
  * @param id                         UUID of the game
  */
final case class SearchGameConfig (
  graphConfig: GraphConfig,
  rounds: Int,
  defenderUnitCount: Int,
  attackerStartingPosition: Int,
  defenderStartingPositions: Vector[Int],
  targets: List[SearchGameTarget],
  defenderVertices: List[List[Int]],
  trackVertices: List[Int],
  coveringUpTracksPossible: Boolean,
  id: UUID
) extends GameConfig {
  override val hashCode : Int = ScalaRunTime._hashCode(this)
  require(graphConfig.edges == graphConfig.edges.distinct)
  //Intersection of vertices allowed for the given defender and the vertices connected to the given vertex
  val possibleDefenderMoves: Vector[Map[Int,List[Int]]] = defenderVertices.map(vertices => vertices.map(
    v =>  (v, graphConfig.edges.filter(e => e.from == v && vertices.contains(e.to)).map(_.to).toList)).toMap).toVector

  val possibleAttackerMoves: List[List[SearchGameAttackerMove]] = Range(0, graphConfig.vertexCount)
    .map(
      v => graphConfig.edges.filter(e => e.from == v).map(e => SearchGameAttackerMove(e.to)).toList
    ).toList


  val defenderInitialState: SearchGameDefenderState =
    SearchGameDefenderState(this, defenderStartingPositions.toList, 0, Set())

  val attackerInitialState: SearchGameAttackerState =
    SearchGameAttackerState(this, attackerStartingPosition, 0)

  val defenderCache: Cache[CachedHashPair[(List[Int], Set[Int]), SearchGameDefenderState], SearchGameDefenderState] =
    Caffeine.newBuilder().build()

  val attackerCache: Cache[CachedHashPair[SearchGameAttackerMove, SearchGameAttackerState], SearchGameAttackerState] =
    Caffeine.newBuilder().build()

  val defenderMoveCache: Cache[List[Int], List[List[Int]]] = Caffeine.newBuilder().build()
}

