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

import pl.edu.pw.mini.sg.game.boundedrationality.{ BoundedRationality }
import pl.edu.pw.mini.sg.game.deterministic.DeterministicGame
import pl.edu.pw.mini.sg.mixed.IterationInfo
import scala.language.existentials

import pl.edu.pw.mini.sg.rng.RandomGenerator
import org.log4s._
import pl.edu.pw.mini.sg.game.deterministic.{PlayerVisibleState, TreeExpander}
import pl.edu.pw.mini.sg.game.deterministic.single.{GameWithPayoff, SinglePlayerGame}
import pl.edu.pw.mini.sg.mixed.{BothTreesPosition, Assessment}
import pl.edu.pw.mini.sg.mixed.tree.{InnerResultNode, MixedStrategyNode}


// TODO czy przekazywac BR do niższych expanderów i jak to robić? 
final case class RandomNodeExpansion(
  subtreeExpandedMultiplier: Double,
  probabilityBoost: Double,
  positiveExpander: TreeExpander[Any, Any, GameWithPayoff],
  negativeExpander: TreeExpander[Any, Any, GameWithPayoff],
  initialExpander: TreeExpander[Any, Any, GameWithPayoff],
  expandWorsePayoff: Boolean
) extends NodeExpansionDecision {
  val logger = getLogger



  final def mul(noExpanded: Boolean) : Double = if(noExpanded) 1 else subtreeExpandedMultiplier

  override def inTreeExpansion[DM, AM, DS <: PlayerVisibleState[DM], AS <: PlayerVisibleState[AM],
  G <: DeterministicGame[DM, AM, DS, AS, G]](
    treePosition: BothTreesPosition[DM, AM, DS, AS, G],
    defenderMixedStrategy: MixedStrategyNode[DM,DS,_],
    desiredAttackerPayoff: InnerResultNode[DM, DS],
    undesiredAttackerPayoff: InnerResultNode[DM, DS],
    noSubtreeExpanded: Boolean 
  )(implicit 
    rng: RandomGenerator, iterationInfo: IterationInfo, br: BoundedRationality
  ) : Boolean = {
    val diff = undesiredAttackerPayoff.payoff.distortedAttacker.attacker - desiredAttackerPayoff.payoff.distortedAttacker.attacker

    if(diff*mul(noSubtreeExpanded) + probabilityBoost > rng.nextDouble()) {
      val unused = treePosition.desiredAttacker.possibleMoves.view.filter(m =>
        defenderMixedStrategy.probabilities.find{case(move, _) => move == m }.isEmpty
      ).toVector
      if(unused.length > 0) {
        val moveToExpand = unused(rng.nextInt(unused.length))
        undesiredAttackerPayoff.expandToLeaf(moveToExpand, treePosition.optimalAttacker.makeMove(moveToExpand), negativeExpander)
        desiredAttackerPayoff.expandToLeaf(moveToExpand, treePosition.desiredAttacker.makeMove(moveToExpand), positiveExpander)
        if(expandWorsePayoff ||
          Assessment.inTreeAssessment(desiredAttackerPayoff, undesiredAttackerPayoff) <
          Assessment.inTreeAssessment(desiredAttackerPayoff.nextNode(moveToExpand),
            undesiredAttackerPayoff.nextNode(moveToExpand)))
          true
        else {
          defenderMixedStrategy.pruneMove(moveToExpand)
          desiredAttackerPayoff.prune()
          undesiredAttackerPayoff.prune()
          false
        }
      }
      else {
//        logger.debug(s"No unused moves, all moves ${treePosition.desiredAttacker.possibleMoves} ${defenderMixedStrategy.probabilities.toList}")
        false
      }
    }
    else
      false
  }

  override def outTreeExpansion[DM, DS <: PlayerVisibleState[DM], G <: SinglePlayerGame[DM, DS, G] with GameWithPayoff](
    gameState: G,
    defenderMixedStrategy: MixedStrategyNode[DM, DS, _],
    undesiredAttackerPayoff: InnerResultNode[DM, DS],
    noSubtreeExpanded: Boolean)
  (
    implicit rng: RandomGenerator,
    iterationInfo: IterationInfo,
    br: BoundedRationality
  ) : Boolean = {
    val diff = undesiredAttackerPayoff.payoff.distortedAttacker.attacker

    if(diff*mul(noSubtreeExpanded) + probabilityBoost > rng.nextDouble()) {
      val unused = gameState.possibleMoves.view.filter(m =>
        defenderMixedStrategy.probabilities.find{case(move, _) => move == m }.isEmpty
      ).toVector
      if(unused.length > 0) {
        logger.trace("Out of tree expanded")
        val moveToExpand = unused(rng.nextInt(unused.length))
        undesiredAttackerPayoff.expandToLeaf(moveToExpand, gameState.makeMove(moveToExpand), negativeExpander)

        if(expandWorsePayoff || Assessment.outOfTreeAssessment(undesiredAttackerPayoff) < Assessment.outOfTreeAssessment(undesiredAttackerPayoff.nextNode(moveToExpand)))
          true
        else {
          defenderMixedStrategy.pruneMove(moveToExpand)
          undesiredAttackerPayoff.prune()
          false
        }
      }
      else
        false
    }
    else
      false
  }

  override def positivePassExpansion[DM, DS <: PlayerVisibleState[DM], G <: SinglePlayerGame[DM, DS, G]  with GameWithPayoff](
    gameState: G,
    defenderMixedStrategy: MixedStrategyNode[DM, DS, _],
    desiredAttackerPayoff: InnerResultNode[DM, DS],
    noSubtreeExpanded: Boolean
  )(
    implicit rng: RandomGenerator,  iterationInfo: IterationInfo
  ): Boolean = {
    val diff = 1 - desiredAttackerPayoff.payoff.real.defender

    if(diff*mul(noSubtreeExpanded) + probabilityBoost > rng.nextDouble()) {
      val unused = gameState.possibleMoves.view.filter(m =>
        defenderMixedStrategy.probabilities.find{case(move, _) => move == m }.isEmpty
      ).toVector
      if(unused.length > 0) {
        logger.trace("Positive phase expanded")
        val moveToExpand = unused(rng.nextInt(unused.length))
        desiredAttackerPayoff.expandToLeaf(moveToExpand, gameState.makeMove(moveToExpand), positiveExpander)

        if (expandWorsePayoff || Assessment.positiveAssessment(desiredAttackerPayoff) < Assessment.positiveAssessment(desiredAttackerPayoff.nextNode(moveToExpand))) 
          true
        else {
          defenderMixedStrategy.pruneMove(moveToExpand)
          desiredAttackerPayoff.prune()
          false
        }
      }
      else
        true      
    }
    else
     true
  }
}
