package pl.edu.pw.mini.game.flipit.deterministic.allpoints

import pl.edu.pw.mini.game.flipit.deterministic.{ Flip, FlipItMove }
import pl.edu.pw.mini.game.flipit.deterministic.noinfo.{Owner, Defender, Attacker}
import pl.edu.pw.mini.sg.game.deterministic.DeterministicGame
import pl.edu.pw.mini.sg.game.graph.Edge

final class AllPointsFlipItGame(
  val vertexOwners: Vector[Owner],
  override val defenderState: AllPointsFlipItDefenderState,
  override val attackerState: AllPointsFlipItAttackerState,
  override val defenderPayoff: Double,
  override val attackerPayoff: Double,
  cache: Cache
) extends DeterministicGame[
  FlipItMove, FlipItMove,
  AllPointsFlipItDefenderState,
  AllPointsFlipItAttackerState,
  AllPointsFlipItGame
] {
  override def makeMove(defenderMove: FlipItMove, attackerMove: FlipItMove) : AllPointsFlipItGame = {
    val attackerFiltered = attackerMove.flippedVertex.filter(x => cache.config.entryNodes.contains(x) ||
      cache.config.graph.edges.exists{case Edge(f, t) => t==x && vertexOwners(f)==Attacker})

    val (newOwners, defenderMoveSuccess, attackerMoveSuccess) = (defenderMove, attackerFiltered) match {
      case (dm: Flip, Some(am)) if dm.vertex == am =>
        (vertexOwners, vertexOwners(dm.vertex)==Defender, vertexOwners(dm.vertex)==Attacker)
      case _ => {
        val f1 : Vector[Owner] => Vector[Owner] = defenderMove.flippedVertex.map(x => (_ : Vector[Owner]).updated(x, Defender)).getOrElse(identity)
        val f2 : Vector[Owner] => Vector[Owner] = attackerFiltered.map(x => (_ : Vector[Owner]).updated(x, Attacker)).getOrElse(identity)
        (f1(f2(vertexOwners)), defenderMove.flippedVertex.nonEmpty, attackerFiltered.nonEmpty)
      }
    }

    val (defenderRoundReward, attackerRoundReward) = newOwners.view.zipWithIndex.foldLeft((0.0, 0.0)){
      case ((d, a), (Defender, idx)) => (d+cache.config.defenderRewards(idx), a)
      case ((d, a), (Attacker, idx)) => (d, a+cache.config.attackerRewards(idx))
    }
    val defenderRoundPayoff = defenderRoundReward-defenderMove.flippedVertex.map(cache.config.defenderCosts).getOrElse(0.0)
    val attackerRoundPayoff = attackerRoundReward-attackerMove.flippedVertex.map(cache.config.attackerCosts).getOrElse(0.0)

    new AllPointsFlipItGame(newOwners,
      defenderState.next(defenderMove, defenderMoveSuccess, defenderRoundPayoff),
      attackerState.next(attackerMove, attackerMoveSuccess, attackerRoundPayoff),
      defenderPayoff+defenderRoundPayoff, attackerPayoff+attackerRoundPayoff, cache)
  }
}
