package pl.edu.pw.mini.sg.mixed.response

import pl.edu.pw.mini.sg.game.boundedrationality.BoundedRationality
import pl.edu.pw.mini.sg.rng.RandomGenerator
import pl.edu.pw.mini.sg.util.Logging
import pl.edu.pw.mini.sg.game.deterministic.{AdvanceablePlayerVisibleState, DeterministicGame, PlayerVisibleState, PureStrategy}
import pl.edu.pw.mini.sg.game.util.attacker.OptimalAttacker
import pl.edu.pw.mini.sg.mixed.StableAttackerFinderState
import pl.edu.pw.mini.sg.mixed.tree.{InnerResultNode, MixedStrategyNode}


trait AttackerResponseWithHistory extends AttackerResponseFinder with Logging {
  val historySize : Int = 50

  val historySkipConfig: HistorySkipConfig

  override def findResponse[
    DM, AM,
    DS <: PlayerVisibleState[DM],
    AS <: AdvanceablePlayerVisibleState[AM, AS],
    G <: DeterministicGame[DM, AM, DS, AS, G]
  ](
    gameRoot: G, defenderStrategy: MixedStrategyNode[DM, DS, _],
    desiredAttackerPayoff: InnerResultNode[DM, DS],
    state: StableAttackerFinderState[AM, AS]
  )(
    implicit rng: RandomGenerator,
    boundedRationality: BoundedRationality
  ): (PureStrategy[AM, AS], StableAttackerFinderState[AM, AS]) = {
    if(historySkipConfig.startUsingHistoryAfter != -1 && historySkipConfig.startUsingHistoryAfter <= state.noNew
      && historySkipConfig.historyUseInterval >= state.skipped) {
      (state.recentAttackers.view.map(_._2).maxBy(s => OptimalAttacker.payoff(gameRoot, defenderStrategy, s).distortedAttacker.attacker), state.copy(skipped = state.skipped+1))
    } else {
      log.trace(s"Attacker find pass start historical: ${state.recentAttackers.size}")
      state.recentAttackers.view.find{case (_, s) =>
        OptimalAttacker.payoff(gameRoot, defenderStrategy, s).distortedAttacker.attacker > desiredAttackerPayoff.payoff.distortedAttacker.attacker + 1e-2}
        .map(x => (x._2, state.register(x._2, historySize))).getOrElse({
          log.trace("Attacker not in history")
          val oa = calculateAttacker[DM, AM, DS, AS, G](gameRoot, defenderStrategy, desiredAttackerPayoff)
          (oa, state.register(oa, historySize))
        })
    }
  }

  def calculateAttacker[
    DM, AM,
    DS <: PlayerVisibleState[DM],
    AS <: AdvanceablePlayerVisibleState[AM, AS],
    G <: DeterministicGame[DM, AM, DS, AS, G]
  ](
    gameRoot: G, defenderStrategy: MixedStrategyNode[DM, DS, _],
    desiredAttackerPayoff: InnerResultNode[DM, DS]
  )(
    implicit rng: RandomGenerator,
    boundedRationality: BoundedRationality
  ) : PureStrategy[AM, AS]
}
