package pl.edu.pw.mini.sg.mixed.adjuster.momentum

import pl.edu.pw.mini.sg.rng.RandomGenerator
import org.log4s._
import pl.edu.pw.mini.sg.game.deterministic.PlayerVisibleState
import pl.edu.pw.mini.sg.mixed.IterationInfo
import pl.edu.pw.mini.sg.mixed.adjuster.StatefulAdjuster
import pl.edu.pw.mini.sg.mixed.tree.{ InnerResultNode, MixedStrategyNode, OptimizerStateType }


case object MomentumAdjuster extends StatefulAdjuster {
  private lazy val logger = getLogger
  override type AdjusterState = Momentum

  implicit val ost: OptimizerStateType[Momentum] = MomentumOptimizerStateType

  private def momentumAdjust[M](
    mixedStrategy: MixedStrategyNode[M, _, Momentum]
  )(
    implicit rng: RandomGenerator, iterationInfo: IterationInfo
  ): Boolean = {
    if(rng.nextDouble() > 0.3) {
      require(mixedStrategy.probabilities.length == mixedStrategy.optimizerState.momentum.length)
      mixedStrategy.probabilities.zip(mixedStrategy.optimizerState.momentum).zipWithIndex.map{
        case (((dm, prob), momentum), idx) => {
          if(momentum < 0) {
            val ch = Math.max(momentum/mixedStrategy.optimizerState.normalizer, -prob)
            require(ch <= 0)
            mixedStrategy.updateProb(idx, prob+ch)
            -ch
          } else
              0.0
        }
      }.sum

      mixedStrategy.probabilities.zip(mixedStrategy.optimizerState.momentum).zipWithIndex.foreach{
        case (((dm, prob), momentum), idx) => {
          if(momentum > 0)
            mixedStrategy.updateProb(idx, prob + momentum/mixedStrategy.optimizerState.normalizer)
        }
      }
      logger.debug(s"${mixedStrategy.state}: Normalized Momentum value: ${mixedStrategy.optimizerState.momentum.map(_/mixedStrategy.optimizerState.normalizer)}")
      logger.debug(s"Normalizer: ${mixedStrategy.optimizerState.normalizer}")
      mixedStrategy.normalizeOrEqual()
      true
    } else false
  }

  /**
    *  @return Czy była zmiana
    */
  override def inTreeAdjust[DM, DS <: PlayerVisibleState[DM]](
    mixedStrategy: MixedStrategyNode[DM, DS, Momentum],
    desiredAttackerPayoff: InnerResultNode[DM, DS],
    optimalAttackerPayoff: InnerResultNode[DM, DS]
  )(
    implicit rng: RandomGenerator, iterationInfo: IterationInfo
  ) : Boolean = {
    val diff = desiredAttackerPayoff.payoff.distortedAttacker.attacker - optimalAttackerPayoff.payoff.distortedAttacker.attacker
    def diffm(move: DM) = desiredAttackerPayoff.payoff(move).distortedAttacker.attacker - optimalAttackerPayoff.payoff(move).distortedAttacker.attacker

    mixedStrategy.optimizerState = mixedStrategy.optimizerState.accumulateAssessments(
      mixedStrategy.probabilities.view.map(p => diffm(p._1) - diff).toVector
    )
    momentumAdjust(mixedStrategy)
  }

   /**
    *  @return Czy była zmiana
    */
 override def outOfTreeAdjust[DM, DS <: PlayerVisibleState[DM]](
    mixedStrategy: MixedStrategyNode[DM, DS, AdjusterState],
    optimalAttackerPayoff: InnerResultNode[DM, DS]
  )(
    implicit rng: RandomGenerator,  iterationInfo: IterationInfo
 ) : Boolean = {
   mixedStrategy.optimizerState = mixedStrategy.optimizerState.accumulateAssessments(
     mixedStrategy.probabilities.view.map(p =>
       optimalAttackerPayoff.payoff.distortedAttacker.attacker - optimalAttackerPayoff.payoff(p._1).distortedAttacker.attacker).toVector
   )
   momentumAdjust(mixedStrategy)
 }


   /**
    *  @return Czy była zmiana
    */
 override def positiveAdjust[DM, DS <: PlayerVisibleState[DM]](
    mixedStrategy: MixedStrategyNode[DM, DS, AdjusterState],
    gamePayoff: InnerResultNode[DM, DS]
  )(
    implicit rng: RandomGenerator,  iterationInfo: IterationInfo
 ) : Boolean = {
   mixedStrategy.optimizerState = mixedStrategy.optimizerState.accumulateAssessments(
     mixedStrategy.probabilities.view.map(p =>
       gamePayoff.payoff(p._1).real.defender - gamePayoff.payoff.real.defender).toVector
   )
   momentumAdjust(mixedStrategy)
 }


}
