package pl.edu.pw.mini.jk.sg.mixeduct

import argonaut.DecodeJson
import org.apache.commons.math3.random.RandomGenerator
import pl.edu.pw.mini.jk.sg.game.common.{ Cloneable, SecurityGame }
import pl.edu.pw.mini.jk.sg.game.common.advanceable.{ AdvanceableAttackerState, AdvanceableDefenderState }
import pl.edu.pw.mini.jk.sg.game.common.{ DefenderMove, SecurityGame }
import pl.edu.pw.mini.jk.sg.game.DefenderAttackerGame
import pl.edu.pw.mini.jk.sg.gteval.GameMatrices
import pl.edu.pw.mini.jk.sg.mixeduct.resulttrends.DecodeResultTrends
import pl.edu.pw.mini.jk.sg.mixeduct.startattackers.{DecodeAttackerProbabilityBuilder, AttackerProbabilityBuilder}
import pl.edu.pw.mini.jk.sg.mixeduct.strategychooser.{StrategyChooserFactory, StrategyChooserFactoryDecode}
import pl.edu.pw.mini.jk.sg.mixeduct.transfer.AttackersInformationTransferFactory
import pl.edu.pw.mini.jk.sg.mixeduct.transfer.prune.PruneJson
import pl.edu.pw.mini.jk.sg.uct.I2UctConfig
import pl.edu.pw.mini.jk.sg.mixeduct.resulttrends.ResultTrendsConfig
import pl.edu.pw.mini.jk.sg.game.GameVariant
import pl.edu.pw.mini.jk.sg.gteval.GameVariantMethodConfig
import pl.edu.pw.mini.jk.sg.gteval.MethodConfigDecoder
import pl.edu.pw.mini.jk.sg.mixeduct.stopcondition.{StopConditionFactory, DecodeStopConditionJson, CombinedMixedUctStopConditionFactory}
import pl.edu.pw.mini.jk.sg.mixeduct.transfer.strategyfilter.StrategyFilterConfig
import pl.edu.pw.mini.jk.sg.mixeduct.attacker.AttackerConfig
import pl.edu.pw.mini.jk.sg.mixeduct.attackerresponse.AttackerSolver
import pl.edu.pw.mini.jk.sg.mixeduct.attackerresponse.AttackerSolverDecode
import pl.edu.pw.mini.jk.sg.uct.tree.PruneTreeFunctions

case class MixedUctConfig (
  stopConditions: List[StopConditionFactory],
  informationTransfer: AttackersInformationTransferFactory,
  startAttacker: AttackerProbabilityBuilder,
  attackerHistory: AttackerConfig,
  strategyFilter: StrategyFilterConfig,
  resultTrends: ResultTrendsConfig,
  gameVariant: GameVariant,
  attackerSolver: AttackerSolver,
  strategyChooser: StrategyChooserFactory,
  i2UctConfig : I2UctConfig[SecurityGame],
  iterationBudget: Int,
  restartLimit: Int,
  interStepPruneFunction: Option[PruneTreeFunctions],
  treeLogConfig: TreeLogConfig
) extends GameVariantMethodConfig  {
  override def name = "Mixeduct"
  //Java compatibility layer (to be romeved in distant future)
  val getStopConditionFactory = CombinedMixedUctStopConditionFactory(stopConditions)
  def getInformationTransfer = informationTransfer
  def getStartAttacker = startAttacker
  def getAttackerHistory = attackerHistory
  def getStrategyFilter = strategyFilter
  def getResultTrends = resultTrends
  def getGameVariant = gameVariant
  def getStrategyChooser = strategyChooser
  def getAttackerSolver = attackerSolver
  def getI2UctConfig = i2UctConfig


  override def performTrainingWithGame[DM <: DefenderMove, AM, AS <: AdvanceableAttackerState[AM], DS <: AdvanceableDefenderState[DM], G <: Cloneable[G] with SecurityGame with DefenderAttackerGame[AM, DM, AS, DS, _]](gameMatrices: GameMatrices[AM, DM, AS, DS, G],
      rng : RandomGenerator) = MixedUctStrategy.uctTrainedPlayerStrategy(this, gameMatrices, rng, treeLogConfig)
}

object MixedUctConfig {
  def apply(stopConditions: List[StopConditionFactory],
    informationTransfer: AttackersInformationTransferFactory,
    startAttacker: AttackerProbabilityBuilder,
    attackerHistory: AttackerConfig,
    strategyFilter: StrategyFilterConfig,
    resultTrends: ResultTrendsConfig,
    gameVariant: GameVariant,
    attackerSolver: AttackerSolver,
    strategyChooser: StrategyChooserFactory,
    i2UctConfig : I2UctConfig[SecurityGame],
    iterationBudget: Int,
    restartLimit: Int,
    interStepPruneFunction: Option[PruneTreeFunctions]) : MixedUctConfig =
    MixedUctConfig(stopConditions, informationTransfer,
      startAttacker, attackerHistory, strategyFilter, resultTrends,
      gameVariant, attackerSolver, strategyChooser,
      i2UctConfig, iterationBudget, restartLimit, interStepPruneFunction,
      TreeLogConfigDisable
    )
}

object MixedUctConfigDecodeJson {
  import MethodConfigDecoder.decodeGameVariant

  def decoder(implicit it: DecodeJson[AttackersInformationTransferFactory],
    ah: DecodeJson[AttackerConfig],
    sf: DecodeJson[StrategyFilterConfig],
    rt: DecodeJson[ResultTrendsConfig],
    as: DecodeJson[AttackerSolver],
    sa: DecodeJson[AttackerProbabilityBuilder],
    sc: DecodeJson[StrategyChooserFactory],
    i2uct: DecodeJson[I2UctConfig[SecurityGame]],
    asc: DecodeJson[StopConditionFactory],
    ptf: DecodeJson[PruneTreeFunctions]
  ) = DecodeJson.jdecode13L(MixedUctConfig.apply)("stopConditions", "informationTransfer", "startAttacker", "attackerHistory", "strategyFilter", "resultTrends", "gameVariant", "attackerSolver", "strategyChooser", "i2Uct", "iterationBudget", "restartLimit", "interStepPruneFunction")

  val defaultDecoder = decoder(AttackersInformationTransferFactory.defaultDecoder,
    AttackerConfig.defaultDecoder, StrategyFilterConfig.defaultDecoder,
    DecodeResultTrends.decode, AttackerSolverDecode.defaultDecode,
    DecodeAttackerProbabilityBuilder.decoder, StrategyChooserFactoryDecode.defaultDecoder, I2UctConfig.defaultDecode, DecodeStopConditionJson.decodeRegularStopContitions, PruneJson.decoder)
}
