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

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.tree.{ InnerResultNode, MixedStrategyNode }
import scala.annotation.tailrec



final object MinimalChangeAdjuster extends StrategyAdjuster {
  private val logger = getLogger

  override def adjust[DM, DS <: PlayerVisibleState[DM]](
    mixedStrategy: MixedStrategyNode[DM, DS, Unit],
    desiredAttackerPayoff: InnerResultNode[DM, DS],
    optimalAttackerPayoff: InnerResultNode[DM, DS],
    rng: RandomGenerator
  )(
      implicit iterationInfo: IterationInfo
  ) : Boolean = adjustT(mixedStrategy, desiredAttackerPayoff, optimalAttackerPayoff, rng)

  def adjustT[DM, DS <: PlayerVisibleState[DM]](
    mixedStrategy: MixedStrategyNode[DM, DS, _],
    desiredAttackerPayoff: InnerResultNode[DM, DS],
    optimalAttackerPayoff: InnerResultNode[DM, DS],
    rng: RandomGenerator
  )(implicit iterationInfo: IterationInfo) : Boolean = {
    val subtreeDiffs: List[(Int, Double, Double, DM)] = mixedStrategy.probabilities.zipWithIndex.map{ case ((move, prob), idx) =>
      (idx, desiredAttackerPayoff.successors(move).payoff.distortedAttacker.attacker - optimalAttackerPayoff.successors(move).payoff.distortedAttacker.attacker, prob, move)
    }.toList.sortBy(_._2)
    val bestProportion = subtreeDiffs.last

    @tailrec
    def step(subtreeDiffs: List[(Int, Double, Double, DM)], rv: Boolean) : Boolean = {
      val diff = optimalAttackerPayoff.payoff.distortedAttacker.attacker - desiredAttackerPayoff.payoff.distortedAttacker.attacker
      if(diff > 0) {
        val first = subtreeDiffs.head
        val diffChangeRate = bestProportion._2 - first._2
        val bestProb = mixedStrategy.probabilities(bestProportion._1)._2
        val change = Math.min(diffChangeRate/diff, first._3)
        mixedStrategy.updateProb(bestProportion._1, bestProb + change)
        mixedStrategy.updateProb(first._1, first._3 - change)
        desiredAttackerPayoff.updateValues()
        optimalAttackerPayoff.updateValues()
        step(subtreeDiffs.tail, true)
      } else {
        rv
      }
    }
    if(bestProportion._2 < 0 || rng.nextDouble()<0.3)
      false
    else
      step(subtreeDiffs, false)
  }
}
