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

import com.google.common.collect.ImmutableList
import org.apache.commons.math3.random.RandomGenerator
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.{ DefenderPosition, MovingTargetsGameConfig }
import pl.edu.pw.mini.sg.util.RangedCache

import scala.collection.JavaConverters._
import scala.runtime.ScalaRunTime


sealed trait CoordDefenderState extends AdvanceableDefenderState[CoordMove] with AdvanceableState[CoordMove, CoordDefenderState] {
  override def advance(move: CoordMove) : CoordDefenderState
  def currentPositions: Vector[DefenderPosition]
  def timeStep: Int
}

object CoordDefenderState {
  def initialState(game: MovingTargetsGameConfig) =
    XMoveDefenderState(
      game,
      0,
      game.defenderStartingPositions,
      Vector(),
      (new RangedCache(XMove.apply), new RangedCache(YMove.apply))
    )
}

case class XMoveDefenderState(
  game: MovingTargetsGameConfig,
  timeStep: Int,
  currentPositions: Vector[DefenderPosition],
  pendingPositions: Vector[DefenderPosition],
  caches: (RangedCache[XMove], RangedCache[YMove])
) extends CoordDefenderState {
  override lazy val getPossibleMoves: ImmutableList[CoordMove] = ImmutableList.copyOf(game.possibleXValues(currentPositions(movingPlayer).x).map(caches._1.get(_)).asJava.asInstanceOf[java.lang.Iterable[XMove]])
  override def advance(move: CoordMove): YMoveDefenderState = move match {
    case m@XMove(newX) if game.checkXValue(currentPositions(movingPlayer).x, newX) => YMoveDefenderState(game, timeStep, currentPositions, pendingPositions, m, caches)
    case _ => throw new IllegalArgumentException("Move not allowed in current state")
  }
  override def getTimeStep(): Int = timeStep

  override def getRandomMove(rng: RandomGenerator) = caches._1.get(game.randomPossibleX(rng)(currentPositions(movingPlayer).x))

  final def movingPlayer = pendingPositions.size

  override lazy val hashCode = ScalaRunTime._hashCode(this)
}

case class YMoveDefenderState(
  game: MovingTargetsGameConfig,
  timeStep: Int, 
  currentPositions: Vector[DefenderPosition],
  pendingPositions: Vector[DefenderPosition],
  xMove: XMove,
  caches: (RangedCache[XMove], RangedCache[YMove])
) extends CoordDefenderState {
  override def getPossibleMoves(): ImmutableList[CoordMove] = ImmutableList.copyOf(game.possibleYValues(currentPositions(movingPlayer), xMove.destination).map(caches._2.get(_)).asJava.asInstanceOf[java.lang.Iterable[CoordMove]])
  override def advance(move: CoordMove): XMoveDefenderState = move match {
    case YMove(y) if game.verifyYValue(currentPositions(movingPlayer), xMove.destination, y) => {
      val newPos = pendingPositions :+ DefenderPosition(xMove.destination, y)
      if(newPos.length == currentPositions.length)
        XMoveDefenderState(game, timeStep+1, newPos, Vector(), caches)
      else
        XMoveDefenderState(game, timeStep, currentPositions, newPos, caches)
    }
    case _ => throw new IllegalArgumentException("Move not allowed in state")
  }
  override def getTimeStep(): Int = timeStep

  override def getRandomMove(rng: RandomGenerator) =
    caches._2.get(game.randomPossibleY(rng)(currentPositions(movingPlayer), xMove.destination))

  final def movingPlayer = pendingPositions.size

  override lazy val hashCode = ScalaRunTime._hashCode(this)
}
