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

import argonaut.DecodeJson
import pl.edu.pw.mini.jk.lib.TraitJsonDecoder
import pl.edu.pw.mini.jk.sg.game.common.DefenderMove
import pl.edu.pw.mini.jk.sg.game.common.advanceable.AdvanceableDefenderState
import pl.edu.pw.mini.jk.sg.uct.tree.{ PruneTreeFunctions, TreeJoiner, TreeJoinerFunctions }
import pl.edu.pw.mini.jk.sg.gteval.GameMatrices
import pl.edu.pw.mini.jk.sg.mixeduct.util.TreeUtils
import pl.edu.pw.mini.jk.sg.gteval.PayoffUtils
import pl.edu.pw.mini.jk.sg.uct.tree.UctNode
import pl.edu.pw.mini.jk.sg.mixeduct.transfer.probabilityfunctions.NodeProbabilityWeightFunction
import pl.edu.pw.mini.jk.sg.mixeduct.transfer.filters.BestPartFilterFactory
import pl.edu.pw.mini.jk.sg.mixeduct.transfer.join.ReadTreeJoinMethod
import pl.edu.pw.mini.jk.sg.mixeduct.transfer.filters.BestPartFilterFactoryDecode
import pl.edu.pw.mini.jk.sg.mixeduct.transfer.probabilityfunctions.NodeProbabilityFunctionDecoder
import pl.edu.pw.mini.jk.sg.mixeduct.transfer.reduction.StatisticsReduction
import pl.edu.pw.mini.jk.sg.mixeduct.transfer.reduction.StatisticsReductionJson
import pl.edu.pw.mini.jk.sg.uct.tree.PayoffTransformer
import pl.edu.pw.mini.jk.sg.mixeduct.transfer.transformers.PayoffTransformerJson

trait AttackersInformationTransferFactory {
  def create[DM <: DefenderMove, DS <: AdvanceableDefenderState[DM]](roundLimit: Int, matrices: GameMatrices[_,DM,_,DS,_], treeJoiner: TreeJoiner) : AttackersInformationTransfer[DM, DS]
}

object AttackersInformationTransferFactory {
  def decoder(decoders: Map[String, DecodeJson[_ <: AttackersInformationTransferFactory]]) = TraitJsonDecoder[AttackersInformationTransferFactory](decoders).decoder


  implicit def fTTdecoder(implicit nwf: DecodeJson[NodeProbabilityWeightFunction], dpf: DecodeJson[PruneTreeFunctions], drd: DecodeJson[StatisticsReduction]) : DecodeJson[FullTreeTransferFactory] = DecodeJson.jdecode4L(FullTreeTransferFactory.apply)("nodeWeightFunction", "pruneFunction", "treeDescentFilterCoefficient", "statisticsReduction")

  implicit val bmfDecoder: DecodeJson[BestMoveFrequencyProbabilityFactory] = DecodeJson.jdecode1L(BestMoveFrequencyProbabilityFactory.apply)("bestMovesCount")

  implicit def bptDecoder(implicit nwf: DecodeJson[NodeProbabilityWeightFunction],
    jmf: DecodeJson[TreeJoinerFunctions], bpf: DecodeJson[BestPartFilterFactory], ptd: DecodeJson[PayoffTransformer]) : DecodeJson[BestPartTransferFactory] = DecodeJson.jdecode6L(BestPartTransferFactory.apply)("nodeWeightFunction", "treeCount", "bestPortion", "joinMethod", "bestPartFilter", "preAddTransformer")

  implicit val defaultDecoder: DecodeJson[AttackersInformationTransferFactory] = {
    implicit val nwf = NodeProbabilityFunctionDecoder.decoder
    implicit val jmf = ReadTreeJoinMethod.decodeFunctions
    implicit val bpf = BestPartFilterFactoryDecode.defaultDecode
    implicit val ptd = PayoffTransformerJson.decode
    decoder(Map("fullTree" -> fTTdecoder(nwf, prune.PruneJson.decoder, StatisticsReductionJson.defaultDecode), "bestMoveFrequency" -> bmfDecoder, "bestPartTransfer" -> bptDecoder))
  }
}
case class FullTreeTransferFactory(nodeWeightFunction: NodeProbabilityWeightFunction, pruneFunction: PruneTreeFunctions, treeDescentFilterCoefficient: Double, statisticsReduction : Option[StatisticsReduction]) extends AttackersInformationTransferFactory {
  override def create[DM <: DefenderMove, DS <: AdvanceableDefenderState[DM]](roundLimit: Int, matrices: GameMatrices[_,DM,_,DS,_], treeJoiner: TreeJoiner) = new FullTreeTransfer(roundLimit, nodeWeightFunction, pruneFunction, treeDescentFilterCoefficient, statisticsReduction.map(_()))
}

case class BestMoveFrequencyProbabilityFactory(bestMovesCount: Int) extends AttackersInformationTransferFactory {
  override def create[DM <: DefenderMove, DS <: AdvanceableDefenderState[DM]](roundLimit: Int, matrices: GameMatrices[_,DM,_,DS,_], treeJoiner: TreeJoiner) = new BestMoveFrequencyProbability(roundLimit, bestMovesCount)
}

case class BestPartTransferFactory(nodeWeightFunction: NodeProbabilityWeightFunction, treeCount: Int,
  bestPortion: Double,
  joinFunctions: TreeJoinerFunctions,
  bestPartFilterFactory: BestPartFilterFactory,
  preAddTransformer: PayoffTransformer
) extends AttackersInformationTransferFactory {
  override def create[DM <: DefenderMove, DS <: AdvanceableDefenderState[DM]](roundLimit: Int, matrices: GameMatrices[_,DM,_,DS,_], treeJoiner: TreeJoiner) = {
    val treeAssessmentFunction =
      (tree: UctNode[DS,DM]) => PayoffUtils.getDefenderPayoff(matrices, TreeUtils.moveSequenceWeights(tree, roundLimit, nodeWeightFunction, 0))
    new BestPartTransfer(roundLimit, bestPortion, nodeWeightFunction, 0, treeCount, treeJoiner, joinFunctions, treeAssessmentFunction, bestPartFilterFactory, preAddTransformer)
  }
}
