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

import com.google.common.collect.ImmutableList
import org.apache.commons.math3.random.RandomGenerator
import org.apache.commons.math3.util.Pair
import pl.edu.pw.mini.jk.lib.TraitJsonDecoder
import argonaut._

import collection.JavaConverters._
import pl.edu.pw.mini.jk.sg.uct.{DecodeSelectionFunctionJson, SelectionFunction}
import pl.edu.pw.mini.jk.sg.mixeduct.attacker.historychanger.AttackerHistoryChanger
import pl.edu.pw.mini.jk.sg.mixeduct.attacker.historychanger.AttackerHistoryDecode

trait AttackerConfig {
  def getAttackerWithHistory[AM](
      rng: RandomGenerator,
      initialStrategy: ImmutableList[Pair[ImmutableList[AM], java.lang.Double]]
  ): AttackerWithHistory[AM]
}

case class ProbabilisticStrategyAttackerConfig(
    historyLength: Int,
    historyChanger: AttackerHistoryChanger
) extends AttackerConfig {
  override def getAttackerWithHistory[AM](
      rng: RandomGenerator,
      initialStrategy: ImmutableList[Pair[ImmutableList[AM], java.lang.Double]]
  ) =
    new ProbabilisticStrategyAttackerWithHistoryFactory[AM](
      initialStrategy,
      historyLength,
      rng,
      historyChanger
    )
}

case class QualityWeightedAttackerConfig(
    strategyPoolSize: Int,
    strategySampleSize: Int,
    selectionFunction: SelectionFunction
) extends AttackerConfig {
  override def getAttackerWithHistory[AM](
      rng: RandomGenerator,
      initialStrategy: ImmutableList[Pair[ImmutableList[AM], java.lang.Double]]
  ) =
    new QualityWeightedHistoryAttacker(
      strategyPoolSize,
      strategySampleSize,
      selectionFunction,
      initialStrategy.asScala.map(p => (p.getFirst, p.getSecond.toDouble)).toList,
      rng
    )
}

object AttackerConfig {
  def probabilisticASDecoder(
      implicit ahcDecode: DecodeJson[AttackerHistoryChanger]
  ): DecodeJson[ProbabilisticStrategyAttackerConfig] =
    DecodeJson.jdecode2L(ProbabilisticStrategyAttackerConfig.apply)(
      "historyLength",
      "historyChanger"
    )

  def qualityWADecoder(
      implicit sfDecode: DecodeJson[SelectionFunction]
  ): DecodeJson[QualityWeightedAttackerConfig] =
    DecodeJson.jdecode3L(QualityWeightedAttackerConfig.apply)(
      "strategyPoolSize",
      "strategySampleSize",
      "selectionFunction"
    )

  def discountingDecoder(
      implicit ahcDecode: DecodeJson[AttackerHistoryChanger]
  ): DecodeJson[DiscountingHistoryAttackerFactoryConfig] =
    DecodeJson.jdecode3L(DiscountingHistoryAttackerFactoryConfig.apply)(
      "historyLength",
      "historyChanger",
      "discountingFactor"
    )

  def acDecoder(
      implicit ascDecode: DecodeJson[AttackerHistoryChanger],
      sfDecode: DecodeJson[SelectionFunction]
  ): DecodeJson[AttackerConfig] =
    TraitJsonDecoder[AttackerConfig](
      Map(
        "simpleHistoryAttacker" -> probabilisticASDecoder,
        "discountingHistoryAttacker" -> discountingDecoder,
        "qualitySelectionHistoryAttacker" -> qualityWADecoder
      )
    ).decoder

  val defaultDecoder =
    acDecoder(AttackerHistoryDecode.defaultDecoder, DecodeSelectionFunctionJson.defaultDecode)
}
