package pl.edu.pw.mini.sg.movingtargets.coverage

import scala.collection.immutable.SortedSet

import org.log4s._
import pl.edu.pw.mini.jk.sg.game.common.advanceable.AdvanceableDefenderState
import pl.edu.pw.mini.jk.sg.game.immutable.AdvanceableState
import pl.edu.pw.mini.sg.movingtargets.{MovingTargetsGameConfig, MovingTargetsImmutableGame}



final case class CoverageDefenderStateCached(
  timeStep: Int,
  coveredTargets: SortedSet[Int],
  successors: Map[SortedSet[Int], CoverageDefenderStateCached]
) extends AdvanceableState[CoverageDefenderMove, CoverageDefenderStateCached] with
    AdvanceableDefenderState[CoverageDefenderMove] {

  import CoverageDefenderStateCached.sortedSetOrdering

  override lazy val getPossibleMoves = MovingTargetsImmutableGame.toImmutableList(successors.keys.toList.sorted.map(CoverageDefenderMove.apply))
  override def getTimeStep = timeStep
  override def advance(move: CoverageDefenderMove): CoverageDefenderStateCached = successors.get(move.coveredTargetIds) match {
    case None => throw new IllegalArgumentException("Move not allowed in state")
    case Some(x) => x
  }
}

object CoverageDefenderStateCached {
  implicit def sortedSetOrdering[T](implicit ot: Ordering[T]) : Ordering[SortedSet[T]] = new Ordering[SortedSet[T]] {
    override def compare(s1: SortedSet[T], s2: SortedSet[T]) : Int = s1.zip(s2).view.map{case (a, b) => ot.compare(a,b)}.find(_ != 0) match {
      case Some(x) => x
      case None => s1.size.compare(s2.size)
    }
  }

  def generateStateTree(game: MovingTargetsGameConfig) : (CoverageDefenderStateCached, CoveragePositionsHelper) = {
    var counter = 0

    val helper = CoveragePositionsHelper(game)
    val logger = getLogger

    def generateNextStep(possiblePositions : List[Int], timeStep: Int, coveredTargets: SortedSet[Int]) : CoverageDefenderStateCached = {
      if(timeStep == game.roundLimit) {
        counter+=1
        if(counter%10000 == 0) logger.debug(s"Leaf $counter")
        CoverageDefenderStateCached(timeStep, coveredTargets, Map())
      }
      else {
        val nextPossiblePositions: List[Int] = helper.allPossibleConfigurations.indices.view
          .filter(cf1 => possiblePositions.exists(cf2 => helper.possibleSuccessions(cf1)(cf2))).toList

        CoverageDefenderStateCached(timeStep, coveredTargets,
          nextPossiblePositions.groupBy(p => helper.positionCoverages(timeStep+1)(p)).map{ case (k, v) => (k, generateNextStep(v, timeStep+1, k))}
        )
      }
    }

    (generateNextStep(List(
      helper.allPossibleConfigurations.indexOf(game.defenderStartingPositions.toList)
    ), 0, game.getCoveredTargetIds(game.defenderStartingPositions, 0)),helper)
  }
}

