package pl.edu.pw.mini.jk.sg.game.immutable.mutable

import scala.annotation.tailrec
import scala.collection.JavaConverters._

import pl.edu.pw.mini.jk.sg.game.DefenderAttackerGame
import pl.edu.pw.mini.jk.sg.game.common.{Accident, Cloneable, DefenderMove, SecurityGame, State, TwoPlayerPayoffInfo}
import pl.edu.pw.mini.jk.sg.game.common.advanceable.{AdvanceableAttackerState, AdvanceableDefenderState}
import pl.edu.pw.mini.jk.sg.game.immutable.{AdvanceableState, ImmutableGame}

final class MutableWrapper[AM, DM <: DefenderMove, AS <: AdvanceableState[AM, AS] with AdvanceableAttackerState[AM], DS <: AdvanceableState[DM, DS] with AdvanceableDefenderState[DM], G <: ImmutableGame[DM, AM, DS, AS, G]] private (
  var currentState: G,
  var defenderMove: Option[DM],
  var attackerMove: Option[AM],
  var accidentHistory: List[Accident],
  initialState: G
) extends SecurityGame with DefenderAttackerGame[AM, DM, AS, DS, State[AS, DS]] with Cloneable[MutableWrapper[AM, DM, AS, DS, G]] {

  def this(initialState: G) =
    this(initialState, None, None, Nil, initialState)



  override def finishRound(): TwoPlayerPayoffInfo = (defenderMove, attackerMove) match {
    case (None, _) => throw new IllegalStateException("Defender has not moved")
    case (_, None) => throw new IllegalStateException("Attacker has not moved")
    case (Some(dm), Some(am)) => {
      val (payoff, newState) = currentState.makeMoves(dm, am)
      currentState = newState
      defenderMove = None
      attackerMove = None
      @tailrec
      def appendAccidentsToList(attacks: Int, defenses: Int, ah: List[Accident]) : List[Accident] =
        if(attacks > 0)
          appendAccidentsToList(attacks -1, defenses, new Accident(currentState.timeStep, 1, -1, true) :: ah)
        else if (defenses > 0)
          appendAccidentsToList(attacks, defenses -1, new Accident(currentState.timeStep, 1, -1, false) :: ah)
        else
          ah

      accidentHistory = appendAccidentsToList(payoff.successfulAttacks, payoff.successfulDefenses, accidentHistory)
      TwoPlayerPayoffInfo(payoff.defenderPayoff, payoff.attackerPayoff, payoff.successfulDefenses, payoff.successfulAttacks)
    }
  }
  override def getAttackerInitialState(): AS = initialState.attackerVisibleState
  override def getAttackerResult(): Double = currentState.attackerPayoff
  override def getCurrentState(): State[AS, DS] =
    new State[AS, DS] {
      override def getDefenderVisibleState: DS = currentState.defenderVisibleState
      override def getAttackerVisibleState: AS = currentState.attackerVisibleState
    }
  override def getDefenderInitialState(): DS = initialState.defenderVisibleState
  override def getDefenderResult(): Double = currentState.defenderPayoff
  override def playAttackerMove(move: AM): Unit = attackerMove match {
    case Some(_) => throw new IllegalStateException("Attaceker already moved")
    case None => attackerMove = Some(move)
  }
  override def playDefenderMove(move: DM): Unit = defenderMove match {
    case Some(_) => throw new IllegalStateException("Defender already moved")
    case None => defenderMove = Some(move)
  }
  override def getPreviousAccidents(): java.util.List[Accident] =
    accidentHistory.asJava

  override def accidentOccured = !accidentHistory.isEmpty
  override def getTimeStep(): Int = currentState.timeStep

  override def typedClone() = new MutableWrapper[AM, DM, AS, DS, G](currentState, defenderMove, attackerMove, accidentHistory, initialState)
}
