package pl.edu.pw.mini.sg.mixed.tree

import pl.edu.pw.mini.sg.game.boundedrationality.probabilitieschanger.{ ProbabilitiesChangerLeaf, ProbabilitiesChangerRegular }
import pl.edu.pw.mini.sg.mixed.IterationInfo
import scala.language.existentials

import pl.edu.pw.mini.sg.rng.RandomGenerator
import pl.edu.pw.mini.sg.game.deterministic.single.{ DefenderGameView, GameWithPayoff }
import pl.edu.pw.mini.sg.game.deterministic.{ DeterministicGame, PlayerVisibleState, PureStrategy, TreeExpander }
import pl.edu.pw.mini.sg.game.Payoff
import pl.edu.pw.mini.sg.game.boundedrationality.BoundedRationality

object CalculateResults {
  // Funkcja która idzie w dół drzewa strategii i drzewa stanów
  // I sprawdza czy sa wszystkie potrzebne gałęzie, a na koniec
  // uaktualnia wypłaty

  // Return info if the tree was expanded
  def calculate[DM, AM,
    DS <: PlayerVisibleState[DM],
    AS <: PlayerVisibleState[AM],
    G <: DeterministicGame[DM, AM, DS, AS, G]]
    (
      gameRoot: G,
      mixedStrategyTree: MixedStrategyNode[DM, DS, _],
      attackerStrategy: PureStrategy[AM, AS],
      resultTree: GameResultNode[DM, DS],
      defenderTreeExpander: TreeExpander[DM, DS, GameWithPayoff],
      expandZeroProb: Boolean
    )(
    implicit rng: RandomGenerator,
      boundedRationality: BoundedRationality
  ) : Boolean = {
    def innerCalc(state: G,
      msNode: MixedStrategyNode[DM, DS, _],
      asNode: PureStrategy[AM, AS],
      resultNode: InnerResultNode[DM, DS]
    ) : Boolean = {
      implicit val dummyIteration = IterationInfo(0,0)
      assert(resultNode.strategyNode == msNode)
      val psl = boundedRationality.probabilitiesChanger match {
        case reg: ProbabilitiesChangerRegular => reg.changeProbability(msNode.probabilities, state.defenderState.possibleMoves)
            //.filter{case (dm, prob) => expandZeroProb || prob > 0 || msNode.probabilities.find(_._1 == dm).map(_._2 > 0).getOrElse(false)}
            .map{case (dm, prob) => (dm, prob, false)}.toVector
        case lm: ProbabilitiesChangerLeaf =>
          state.defenderState.possibleMoves.map(m => {
            val prob = msNode.probabilities.find(_._1==m).map(_ => false).getOrElse(true)
            (m, -1, prob)
          }).toVector
      }
      val expandedOuter =  psl.map{case (dm, _, last) => {
        val stateSucc: G = state.makeMove(dm, asNode.move)
        if(stateSucc.isLeaf) {
          val succ = resultNode.successors.getOrElseUpdate(
            dm,
            new LeafNode(new Payoff(stateSucc.defenderPayoff, stateSucc.attackerPayoff),
              stateSucc.defenderState)
          )

          succ match {
            case succ : LeafNode[DM, DS] => {
              assert(succ.state == stateSucc.defenderState)
              assert(stateSucc.defenderPayoff == succ.payoff.real.defender)
              assert(stateSucc.attackerPayoff == succ.payoff.real.attacker)
            }
            case _ => throw new IllegalStateException("Leaf node expected")
          }
          false
        } else if (!last) {
          val (msSucc, expanded) = msNode.nextState(dm, stateSucc.defenderState, defenderTreeExpander,
            new DefenderGameView[DM, AM, DS, AS, G](stateSucc, asNode.nextState(stateSucc.attackerState))
          )()
          val succ = resultNode.successors.getOrElseUpdate(dm,
            new InnerResultNode(msSucc, stateSucc.defenderState))

          val ee = succ match {
            case succ : InnerResultNode[DM, DS] => {
              val ex = innerCalc(stateSucc, msSucc, asNode.nextState(stateSucc.attackerState), succ)
              succ.updateValues()
              ex
            }
            case _ => throw new IllegalStateException("InnerResultNode expected")
          }
          expanded || ee
        } else {
          false
        }
      }}.exists(identity)
      resultNode.updateValues()
      expandedOuter
    }
    val expanded3 = innerCalc(gameRoot, mixedStrategyTree, attackerStrategy, resultTree.asInstanceOf[InnerResultNode[DM, DS]])
    resultTree.updateValues()
    expanded3
  }

}
