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

import pl.edu.pw.mini.game.flipit.deterministic.FlipItConfig
import pl.edu.pw.mini.sg.game.deterministic.DeterministicGame
import pl.edu.pw.mini.sg.game.graph.Edge
import pl.edu.pw.mini.game.flipit.deterministic.{FlipItMove, Flip, NoOp}

sealed trait Owner
object Defender extends Owner
object Attacker extends Owner

// Czy dopuszczać ruchy atakującego, ktore na pewno nic nie dają??
final class NoInfoFlipItGame(
  config: FlipItConfig,
  nodeOwners: Vector[Owner],
  cumulativeDefenderReward: Double,
  cumulativeAttackerReward: Double,
  override val defenderState: NoInfoFlipItState,
  override val attackerState: NoInfoFlipItAttackerState,
) extends DeterministicGame[
  FlipItMove, FlipItMove, NoInfoFlipItState,
  NoInfoFlipItAttackerState, NoInfoFlipItGame
]{
  def attackerPayoff: Double = cumulativeAttackerReward
  def defenderPayoff: Double = cumulativeDefenderReward
  def makeMove(defenderMove: FlipItMove,attackerMove: FlipItMove): NoInfoFlipItGame = {
    val attackerFiltered = attackerMove.flippedVertex.filter(x => config.entryNodes.contains(x) ||
      config.graph.edges.exists{case Edge(f, t) => t==x && nodeOwners(f)==Attacker})
    val updatedOwners =
      if(defenderMove.flippedVertex.flatMap(dv => attackerFiltered.map(av => av==dv)).getOrElse(false)) {
        nodeOwners
      } else {
        val defUpdate : Vector[Owner] => Vector[Owner] = defenderMove
          .flippedVertex.map(i => (x : Vector[Owner]) => x.updated(i, Defender)).getOrElse(identity _)
        val attUpdate : Vector[Owner] => Vector[Owner] = attackerFiltered
          .map(i => (x : Vector[Owner]) => x.updated(i, Attacker)).getOrElse(identity _)
        defUpdate(attUpdate(nodeOwners))
      }
    val defenderCost = defenderMove.flippedVertex.map(config.defenderCosts).getOrElse(0.0)
    val attackerCost = attackerMove.flippedVertex.map(config.attackerCosts).getOrElse(0.0)
    val (defenderIncome, attackerIncome) = updatedOwners.view.zipWithIndex.foldLeft((0.0, 0.0)){
      case ((di, ai), (Defender, idx)) => (di+config.defenderRewards(idx), ai)
      case ((di, ai), (Attacker, idx)) => (di, ai+config.attackerRewards(idx))
    }
    new NoInfoFlipItGame(config, updatedOwners,
      cumulativeDefenderReward+defenderIncome-defenderCost,
      cumulativeAttackerReward+attackerIncome-attackerCost,
      defenderState.nextState, attackerState.nextState(attackerMove)
    )
  }
}

