package pl.edu.pw.mini.sg.compactlp.util

import java.util.UUID

import argonaut.Argonaut.casecodec2
import argonaut.{CodecJson, EncodeJson}
import squants.time.{Seconds, Time}

final case class CompactLpMilpInfo(
                                    M: Double,
                                    M2: Double,
                                    s: Int,
                                    cpuInfo: String,
                                    gitHash: String = petrus.mtgcompactlp.BuildInfo.gitCommit
                                  )

object CompactLpMilpInfo {
  implicit val encodeJson: EncodeJson[CompactLpMilpInfo] =
    EncodeJson.jencode5L((CompactLpMilpInfo.unapply _).andThen(_.get))(
      "M", "M2", "s", "cpuInfo", "gitHash"
    ).mapJson(j => {
      import argonaut.Argonaut._
      ("method" := "CompactLP MILP") ->: j
    })
}

final case class Attacker(target: Int, startTime: Int)

final case class Position(x: Double, y: Double)

final case class PureStrategy(probability: Double, positions: List[List[Position]])

final case class Strategy(game: UUID, mixedStrategy: List[PureStrategy])

final case class TrainingResult(
                                 optimalAttacker: Option[Attacker],
                                 method: CompactLpMilpInfo,
                                 defenderPayoff: Double,
                                 attackerPayoff: Double,
                                 strategy: Strategy,
                                 preprocessTime: Time,
                                 trainingTime: Time)


object TrainingResult {
  implicit def encodeSeconds: CodecJson[Time] = CodecJson.derived[Double].xmap[Time](Seconds(_))(_.toSeconds)

  implicit val encodeJson: EncodeJson[TrainingResult] =
    EncodeJson.jencode7L((TrainingResult.unapply _).andThen(_.get))(
      "optimalAttacker", "method", "defenderPayoff", "attackerPayoff", "strategy", "preprocessingTime", "trainingTime"
    )

  implicit def encodeAttacker: CodecJson[Attacker] =
    casecodec2(Attacker.apply, Attacker.unapply)("target", "startTime")

  implicit def encodePosition: CodecJson[Position] =
    casecodec2(Position.apply, Position.unapply)(
      "x", "y"
    )

  implicit def encodePureStrategy: CodecJson[PureStrategy] =
    casecodec2(PureStrategy.apply, PureStrategy.unapply)(
      "probability", "positions"
    )

  implicit def encodeStrategy: CodecJson[Strategy] =
    casecodec2(Strategy.apply, Strategy.unapply)(
      "game", "mixedStrategy"
    )
}
