package pl.edu.pw.mini.sg.se


import pl.edu.pw.mini.sg.game.deterministic.plugin.GameParams
import scala.collection.mutable.{ArrayBuffer, Map => MMap}

import argonaut.{CursorHistory, DecodeJson, DecodeResult, Json}
import pl.edu.pw.mini.sg.game.boundedrationality.{BoundedRationality, BoundedRationalityJson, DistortedPayoff}
import pl.edu.pw.mini.sg.game.boundedrationality.payoffchanger.PayoffChangerJson
import pl.edu.pw.mini.sg.game.boundedrationality.probabilitieschanger.ProbabilitiesChangerJson
import pl.edu.pw.mini.sg.game.deterministic.{AdvanceablePlayerVisibleState, DeterministicGame, PlayerVisibleState}
import pl.edu.pw.mini.sg.game.util.attacker.OptimalAttacker
import pl.edu.pw.mini.sg.mixed.IterationInfo
import pl.edu.pw.mini.sg.mixed.tree.MixedStrategyNode
import pl.edu.pw.mini.sg.results.{MethodInfo, NewMixedMethodInfo}
import pl.edu.pw.mini.sg.rng.RandomGenerator
import scalaz.{NonEmptyList, Validation}
import squants.Time
import squants.time.Nanoseconds



class UniformStrategyMethod(boundedRationality: BoundedRationality) extends StrategyMethod {
  def findBehavioralStrategy[
    DM, AM,
    DS <: PlayerVisibleState[DM],
    AS <: AdvanceablePlayerVisibleState[AM, AS],
    G <: DeterministicGame[DM, AM, DS, AS, G]
  ](
    game: G, params: GameParams, rng: RandomGenerator
  ) : (MixedStrategyNode[DM, DS, Unit], DistortedPayoff, Time) = {
    val defenderState = game.defenderState
    val uniformProb = 1.0/defenderState.possibleMoves.length.toDouble
    val uniformStrategy = new MixedStrategyNode[DM, DS, Unit](defenderState,
      ArrayBuffer(defenderState.possibleMoves.map(dm => (dm, uniformProb, IterationInfo.dummyIterationInfo)) : _*),
      MMap.empty)(MixedStrategyNode.unitST)
    val (attacker, payoff) = OptimalAttacker.calculate[DM, AM, DS, AS, G](game, uniformStrategy)(boundedRationality)
    (uniformStrategy, payoff, Nanoseconds(0.0))
  }

  override def getMethodInfo(configFileName: String, gitHash: String, version: String): MethodInfo = {
    NewMixedMethodInfo(configFileName, gitHash, version)
  }
}

object UniformStrategyMethodParser extends StrategyMethodParser[UniformStrategyMethod] {
  def tryParse(config: Json) : Validation[NonEmptyList[(String, CursorHistory)], UniformStrategyMethod] = {
    import BoundedRationalityJson.boundedRationalityCodec
    import PayoffChangerJson.payoffChangerCodec
    import ProbabilitiesChangerJson.probabilitiesChangerCodec
        val decoder = DecodeJson(hCursor => {
      val res : DecodeResult[UniformStrategyMethod] = (for {
        res <- (hCursor--\("uniform")).as[String].flatMap[BoundedRationality => UniformStrategyMethod](s => if(s=="uniform") DecodeResult.ok(new UniformStrategyMethod(_)) else DecodeResult.fail("uniform field must have uniform value", hCursor.history))
        br <- (hCursor--\("boundedRationality")).as[BoundedRationality]
      } yield (res(br)))      
      res
    })

    Validation.fromEither(decoder.decodeJson(config).toEither).toValidationNel
  }

  override val name = "uniform"
}
