package petrus

import argonaut.Parse
import ch.qos.logback.classic.{Level, LoggerContext}
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.FileAppender
import org.scalactic.TolerantNumerics
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpec}
import org.slf4j.Logger
import pl.edu.pw.mini.sg.game.boundedrationality.BoundedRationality
import pl.edu.pw.mini.sg.game.boundedrationality.payoffchanger.NeutralPayoffChanger
import pl.edu.pw.mini.sg.game.boundedrationality.probabilitieschanger.SimplifiedAnchoringTheory
import pl.edu.pw.mini.sg.game.graph.sud.{ SingleUnitAttackerState, SingleUnitDefenderState }
import pl.edu.pw.mini.sg.game.graph.{DeterministicGraphGameConfig, DeterministicGraphGameConfigJson}
import pl.edu.pw.mini.sg.game.graph.sud.{DeterministicSingleUnitMoveGraphGame, SingleUnitDefenderState}
import pl.edu.pw.mini.sg.game.util.MoveStateKey
import pl.edu.pw.mini.sg.game.util.attacker.OptimalAttacker
import pl.edu.pw.mini.sg.mixed.IterationInfo
import pl.edu.pw.mini.sg.mixed.tree.MixedStrategyNode
import pl.edu.pw.mini.sg.mixed.tree.MixedStrategyNode.unitST

import scala.io.Source

class SimplifiedAnchoringTheoryPayoffSpec extends WordSpec with Matchers with BeforeAndAfterAll {
  override def beforeAll() = {
    ch.qos.logback.classic.BasicConfigurator.configureDefaultContext()
    val context: LoggerContext = org.slf4j.LoggerFactory.getILoggerFactory().asInstanceOf[LoggerContext];
    val rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME)
    rootLogger.detachAndStopAllAppenders()
    rootLogger.setLevel(Level.INFO)
    rootLogger.addAppender({
      val appender = new FileAppender[ILoggingEvent]()
      appender.setFile("graph-test.log")
      appender.setContext(context)
      appender.setEncoder({
        val enc = new PatternLayoutEncoder()
        enc.setContext(context)
        enc.setPattern("[%thread] %-20logger %level - %msg%n")
        enc.start()
        enc
      })
      appender.start()
      appender
    })
    val peaLogger = context.getLogger("pl.edu.pw.mini.sg.mixed")
    peaLogger.setLevel(Level.DEBUG)
    //    context.getLogger("pl.edu.pw.mini.sg.mixed.MixedMethod$").setLevel(Level.DEBUG)
    //    context.getLogger("pl.edu.pw.mini.sg.mixed.UctAttackerSampler$").setLevel(Level.DEBUG)
    //    context.start();
  }

  implicit val ii = IterationInfo.dummyIterationInfo
  implicit val doubleEquality = TolerantNumerics.tolerantDoubleEquality(0.003)


  def readGame(file: String): DeterministicSingleUnitMoveGraphGame = {
    import DeterministicGraphGameConfigJson.deterministicGraphGameConfigCodec
    val gameConfig: DeterministicGraphGameConfig = Parse.decode[DeterministicGraphGameConfig](
      Source.fromInputStream(getClass.getResourceAsStream(file)).mkString
    ) match {
      case Right(v) => v
      case Left(err) => throw new RuntimeException(err.toString)
    }

    DeterministicSingleUnitMoveGraphGame(gameConfig)
  }

  "simplified anchoring" when {
    implicit val boundedRationality = BoundedRationality(NeutralPayoffChanger, SimplifiedAnchoringTheory(0.5))
    "playing game smallbuilding-20-5" should {
      val game = readGame("smallbuilding-20-5.ggame")


      val strategy = new MixedStrategyNode[Int, SingleUnitDefenderState, Unit](game.defenderState)(
        MixedStrategyNode.unitST);
      {
        strategy.addMove(1, 1.0)
        strategy.addSuccessor(new MoveStateKey(1, game.defenderState.continuation(1)),
          {
            val state1 = game.defenderState.continuation(1).continuation(1)
            val node1 = new MixedStrategyNode[Int, SingleUnitDefenderState, Unit](game.defenderState.continuation(1))
            node1.addMove(1, 1.0)
            node1.addSuccessor(new MoveStateKey(1, state1),
              {
                val node2 = new MixedStrategyNode[Int, SingleUnitDefenderState, Unit](state1)
                node2.addMove(1, 1.0)
                val state2 = game.defenderState.continuation(1).continuation(1).continuation(1)
                node2.addSuccessor(new MoveStateKey(1, state2),
                  {
                    val node3 = new MixedStrategyNode[Int, SingleUnitDefenderState, Unit](state2)
                    node3.addMove(1, 1.0)
                    val state3 = game.defenderState.continuation(1).continuation(1).continuation(1).continuation(1)
                    node3.addSuccessor(new MoveStateKey(1, state3),
                      {
                        val node4 = new MixedStrategyNode[Int, SingleUnitDefenderState, Unit](state3)
                        node4.addMove(1, 1.0)
                        val state4 = game.defenderState.continuation(1).continuation(1).continuation(1).continuation(1).continuation(1)

                        node4.addSuccessor(new MoveStateKey(1, state4),
                          {
                            val state5 = game.defenderState.continuation(1).continuation(1).continuation(1).continuation(1).continuation(1)
                            new MixedStrategyNode[Int, SingleUnitDefenderState, Unit](state5)
                          })

                        node4
                      })
                    node3
                  })
                node2
              })
            node1
          })
      };

      "give payoff of (0.67, -0.1)" in {
        val (attacker, payoff) = OptimalAttacker.calculate[Int, Int, SingleUnitDefenderState, SingleUnitAttackerState, DeterministicSingleUnitMoveGraphGame](game, strategy)
        payoff.real.defender === (0.67)
        payoff.real.attacker === (-0.1)

      }
    }

  }
}
