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

import argonaut.{CursorHistory, Json}
import pl.edu.pw.mini.sg.game.boundedrationality.DistortedPayoff
import pl.edu.pw.mini.sg.game.deterministic.plugin.GameParams
import pl.edu.pw.mini.sg.rng.RandomGenerator
import pl.edu.pw.mini.sg.bosansky.DeterministicGameUtilsProvider
import pl.edu.pw.mini.sg.correlated._
import pl.edu.pw.mini.sg.game.Payoff
import pl.edu.pw.mini.sg.game.deterministic.{AdvanceablePlayerVisibleState, DeterministicGame, PlayerVisibleState}
import pl.edu.pw.mini.sg.mixed.tree.MixedStrategyNode
import pl.edu.pw.mini.sg.results.MethodInfo
import scalaz.{NonEmptyList, Validation}
import squants.time.Time

class CorrelatedStrategiesMethod(config: CorrelatedStrategiesConfig) extends StrategyMethod {
  private val solver = config.solverConfig.solverFromConfig

  override 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 utils = new DeterministicGameUtilsProvider[DM, AM, DS, AS, G]
    val gameSolver = new CorrelatedStrategiesSolver(solver, utils, config.restrictionGenerator)
    val (msn, payoff, time) = gameSolver.findStrategy(game, config.timeLimit)
    (msn, new DistortedPayoff(payoff, new Payoff(Double.NaN, Double.NaN)), time)
  }

  override def getMethodInfo(configFileName: String, gitHash: String, version: String): MethodInfo = {
    CorrelatedStrategiesMethodInfo(configFileName, gitHash, version, solver.solverInfo, config.restrictionGenerator)
  }
}

object CorrelatedStrategiesMethodParser extends StrategyMethodParser[CorrelatedStrategiesMethod] {
  override def tryParse(config: Json): Validation[NonEmptyList[(String, CursorHistory)], CorrelatedStrategiesMethod] = {
    Validation.fromEither(config.as(CorrelatedStrategiesConfigJson.correlatedStrategiesConfigCodec).toEither).map(new CorrelatedStrategiesMethod(_)).toValidationNel
  }

  override val name = "correlated"
}
