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

import java.io.File

import argonaut.Argonaut._
import argonaut.{Parse, _}
import better.files.FileOps
import org.rogach.scallop.{ScallopConf, ScallopOption}
import pl.edu.pw.mini.sg.solver.gurobisolver.{GurobiConfig, GurobiSolver}
import pl.edu.pw.mini.sg.compactlp.util.{CompactLpMilpInfo, PureStrategy, Strategy, TrainingResult}
import pl.edu.pw.mini.sg.game.results.CpuInfo
import pl.edu.pw.mini.sg.movingtargets.MovingTargetsGameConfigReader.gameDecode
import pl.edu.pw.mini.sg.movingtargets.{MovingTargetsGameConfig, MovingTargetsGameConfigReader}
import pl.edu.pw.mini.sg.solver.SolverConfig
import squants.time.Nanoseconds

object CompactLP {

  private val eps = 0.0001

  def main(args: Array[String]): Unit = {
    val options = new CmdOptions(args)
    val fileSource = io.Source.fromFile(options.input())
    val gameConfig = Parse.decode[MovingTargetsGameConfig](fileSource.mkString) match {
      case Right(c) => c
    }
    fileSource.close()

    val configFile = io.Source.fromFile(options.config())
    val config = Parse.decode(configFile.mkString)(CompactLPConfigJson.compactLPConfigCodec) match {
      case Right(c: CompactLPConfig) => c
    }
    configFile.close()
    val solver = config.solverConfig.solverFromConfig

    val compactLpSolver = new CompactLPSolver(solver, new SparseStrategyGenerator(eps), eps)
    val (preTime, solveTime, defenderPayoff, attackerPayoff, solverDefenderPayoff, solverAttackerPayoff, optimalAttacker, mixedStrategy) = compactLpSolver.solve(new MovingTargetsGameWrapper(gameConfig), config.sparsity)

    solver.close()

    val strategy = if (options.emptyStrategy()) List.empty[PureStrategy] else mixedStrategy

    val result = TrainingResult(optimalAttacker,
      CompactLpMilpInfo(2.0, 3.0, config.sparsity, CpuInfo()),
      defenderPayoff,
      attackerPayoff,
      Strategy(gameConfig.id, strategy),
      preTime,
      solveTime)

    options.output().toScala.write(result.asJson.toString())

    if (options.solverOutput.isDefined) {
      val solverResult = TrainingResult(optimalAttacker,
        CompactLpMilpInfo(2.0, 3.0, config.sparsity, CpuInfo()),
        solverDefenderPayoff,
        solverAttackerPayoff,
        Strategy(gameConfig.id, strategy),
        preTime,
        solveTime)

      options.solverOutput().toScala.write(solverResult.asJson.toString())
    }

  }
}


private[compactlp] class CmdOptions(args: Array[String]) extends ScallopConf(args) {
  def input: ScallopOption[File] = opt[File](name = "input", short = 'i', required = true)

  val output: ScallopOption[File] = opt[File](name = "output", short = 'o', argName = "file", descr = "Training result output file", required = true)
  val solverOutput: ScallopOption[File] = opt[File](name = "solverOutput", argName = "file", descr = "Training result output file with payoffs from solver")
  val emptyStrategy: ScallopOption[Boolean] = opt[Boolean](name = "emptyStrategy", short = 'e', descr = "Do not generate strategy in json")
  val config: ScallopOption[File] = opt[File](name = "config", short='c', descr = "Configuration file", required = true)

  verify()
}
