package petrus.mtg.integration

import argonaut.{ Json, JsonIdentity }
import java.io.{ File, FileWriter }
import java.util.UUID
import org.scalatest.BeforeAndAfterAll

import scala.io.Source

import argonaut.{Parse, JsonIdentity}
import canes.game.{Depot, MovingTargetsGame, StrategyVerifier, Target, TrainingResultJson}
import org.apache.commons.math3.random.Well19937c
import org.scalatest.{WordSpec, Matchers}
import pl.edu.pw.mini.jk.sg.gteval.config.GtevalOptions
import pl.edu.pw.mini.jk.sg.mixeduct.{MixedUctConfig, MixedUctConfigDecodeJson}
import pl.edu.pw.mini.sg.movingtargets.MovingTargetsGameConfig
import squants.time.Milliseconds


class StrategyVerificationSpec extends WordSpec with Matchers with BeforeAndAfterAll {
  val gameSpec = MovingTargetsGame(UUID.randomUUID(),
    depots = List(Depot(1, 1, 1), Depot(2, 3, 3)),
    targets = List(Target(1, 1, 2, List(1), 5, -2, -2, 3)),
    targetSpeed = 1,
    defenderCount = 2,
    defenderSpeed = 2,
    defenderStartDepots = List(1,1),
    defenderRange = 1,
    gameTime = 5,
    timeInterval = 1,
    attackLengthRealtime = 0)

  implicit val dec =  MixedUctConfigDecodeJson.defaultDecoder
  val mixedUctConfigSingle : MixedUctConfig =
    Parse.decodeOr[MixedUctConfig, MixedUctConfig](Source.fromURL(getClass().getResource("single.json")).mkString, identity, ???)

  val mixedUctConfigMulti : MixedUctConfig =
    Parse.decodeOr[MixedUctConfig, MixedUctConfig](Source.fromURL(getClass().getResource("multi.json")).mkString, identity, ???)

    val mixedUctConfigCoord : MixedUctConfig =
    Parse.decodeOr[MixedUctConfig, MixedUctConfig](Source.fromURL(getClass().getResource("coord.json")).mkString, identity, ???)

  val mixedUctConfigFHL : MixedUctConfig =
    Parse.decodeOr[MixedUctConfig, MixedUctConfig](Source.fromURL(getClass().getResource("limited-tree-coord.json")).mkString, identity, ???)

  val file: File = File.createTempFile("movingtargets", ".sgmt")
  val writer = new FileWriter(file);
  writer.write(JsonIdentity.ToJsonIdentity(gameSpec).asJson(canes.game.MovingTargetsGameConfigReader.gameDecode).toString)
  writer.close()

  override def afterAll : Unit = file.delete()

  val evalOpts = new GtevalOptions(file, null, mixedUctConfigSingle, Json.jEmptyObject, java.util.Optional.empty())


  "normal game training result" should {
    val rng = new Well19937c(123)
    val result = mixedUctConfigSingle.performTraining(evalOpts.getGameVariantRunner(), rng).methodResult.getTrainingResultAsJson("mixeduct", Json.jEmptyObject, Milliseconds(0))
    val resultParsed = result.as(TrainingResultJson.TrainingResultCodec).getOr(???)

    "verify against game" in {
      StrategyVerifier.verifyStrategy(gameSpec, resultParsed.strategy) should equal(Right(()))
    }

    "have similar payoffs" in {
      resultParsed.verifyPayoff(gameSpec).isRight should be(true)
    }
  }

  "multi-move game training result" should {
    val rng = new Well19937c(123)
    val result = mixedUctConfigMulti.performTraining(evalOpts.getGameVariantRunner(), rng).methodResult.getTrainingResultAsJson("mixeduct", Json.jEmptyObject, Milliseconds(0))
    val resultParsed = result.as(TrainingResultJson.TrainingResultCodec).getOr(???)

    "verify against game" in {
      StrategyVerifier.verifyStrategy(gameSpec, resultParsed.strategy) should equal(Right(()))
    }

    "have similar payoffs" in {
      resultParsed.verifyPayoff(gameSpec).isRight should be(true)
    }
  }

  "coord-limited-tree game training result" should {
    val rng = new Well19937c(123)
    val result = mixedUctConfigFHL.performTraining(evalOpts.getGameVariantRunner(), rng).methodResult.getTrainingResultAsJson("mixeduct", Json.jEmptyObject, Milliseconds(0))
    val resultParsed = result.as(TrainingResultJson.TrainingResultCodec).getOr(???)

    "verify against game" in {
      StrategyVerifier.verifyStrategy(gameSpec, resultParsed.strategy) should equal(Right(()))
    }

    "have similar payoffs" in {
      resultParsed.verifyPayoff(gameSpec).isRight should be(true)
    }
  }


  "coord-move game training result" should {
    val rng = new Well19937c(123)
    val result = mixedUctConfigCoord.performTraining(evalOpts.getGameVariantRunner, rng).methodResult.getTrainingResultAsJson("mixeduct", Json.jEmptyObject, Milliseconds(0))
    val resultParsed = result.as(TrainingResultJson.TrainingResultCodec).getOr(???)

    "verify against game" in {
      StrategyVerifier.verifyStrategy(gameSpec, resultParsed.strategy) should equal(Right(()))
    }

    "have similar payoffs" in {
      resultParsed.verifyPayoff(gameSpec).isRight should be(true)
    }

  }
}
