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

import com.google.common.collect.ImmutableList
import pl.edu.pw.mini.jk.sg.game.common.DefenderMove
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, MovingTargetsDefenderMove, MovingTargetsDefenderState, MovingTargetsGameConfig }
import scala.annotation.tailrec
import scala.collection.JavaConverters._

sealed trait AggregateMTGDefenderMove extends DefenderMove

final object NoopMove extends AggregateMTGDefenderMove {
  val immList : ImmutableList[AggregateMTGDefenderMove] = ImmutableList.of(NoopMove)
}
final case class ShiftMove(
  shift: Vector[(Int, Int)]
) extends AggregateMTGDefenderMove {
}

sealed abstract class AggregateMTGDefenderState(
  val states: Vector[MovingTargetsDefenderState],
  val config: MovingTargetsGameConfig
)  extends AdvanceableDefenderState[AggregateMTGDefenderMove] with AdvanceableState[AggregateMTGDefenderMove, AggregateMTGDefenderState]

final case class AggregateMTGDefenderDecisionState(
  aggregation: Int,
  override val states: Vector[MovingTargetsDefenderState],
  override val config: MovingTargetsGameConfig,
  precomputedMoves : ImmutableList[AggregateMTGDefenderMove]
) extends AggregateMTGDefenderState(states, config) {
  require(aggregation > 1)

  def advance(move: AggregateMTGDefenderMove): AggregateMTGDefenderState = {
    val shifts = move match {
      case ShiftMove(shifts) if shifts.forall{ case (dx, dy) =>
        config.checkXValue(0, dx) && config.verifyYValue(DefenderPosition(0,0), dx, dy)
      } => shifts
      case _ => throw new IllegalArgumentException(s"Move $move not allowed in current state")
    }

    val advancedStates = states.map(s => {
      val move = s.currentPositions.zip(shifts)
        .map{case (DefenderPosition(px, py), (dx, dy)) =>
          DefenderPosition.cached(px+dx, py+dy)}.toVector
      s.advance(MovingTargetsDefenderMove(move))
    })

    AggregateMTGDefenderContinuationState(aggregation, advancedStates, shifts, 1, config, precomputedMoves)
  }
  override def getPossibleMoves(): ImmutableList[AggregateMTGDefenderMove] = precomputedMoves
  override def getTimeStep(): Int = states.head.getTimeStep
}

final case class AggregateMTGDefenderContinuationState(
  aggregation: Int,
  override val states: Vector[MovingTargetsDefenderState],
  chosenShifts: Vector[(Int, Int)],
  step: Int,
  override val config: MovingTargetsGameConfig,
  precomputedMoves : ImmutableList[AggregateMTGDefenderMove]
) extends AggregateMTGDefenderState(states, config) {
  require(aggregation > 1)

  def advance(x$1: AggregateMTGDefenderMove): AggregateMTGDefenderState = {
    val advancedStates = states.map(s => {
      val move = s.currentPositions.zip(chosenShifts).map{case (DefenderPosition(px, py), (dx, dy)) => DefenderPosition.cached(px+dx, py+dy)}.toVector
    s.advance(MovingTargetsDefenderMove(move))
  })

    if(step+1 == aggregation)
      AggregateMTGDefenderDecisionState(aggregation,
        advancedStates,
        config, precomputedMoves)
    else {
      AggregateMTGDefenderContinuationState(aggregation, advancedStates, chosenShifts, step+1, config, precomputedMoves)
    }
  }
  override def getPossibleMoves(): ImmutableList[AggregateMTGDefenderMove] =
    NoopMove.immList
  override def getTimeStep(): Int = states.head.getTimeStep
}

object AggregateMTGDefenderState {
  def newGame(config: MovingTargetsGameConfig, aggregation: Int)
      : AggregateMTGDefenderState = {
   val range =  Range.inclusive(-config.possibleStepsInTime, config.possibleStepsInTime)
    val possibleSingleOffsets = range.flatMap(x =>
      range.map(y => (x,y))).filter {
      case (x,y) => config.checkXValue(0, x) && config.verifyYValue(DefenderPosition(0,0), x, y)
    }.toVector

    val possibleOffsets = {
      @tailrec def cartesian(rep: Int, res: Vector[Vector[(Int, Int)]]) : Vector[Vector[(Int, Int)]] =
        if(rep==config.defenderCount) res
        else cartesian(rep+1, res.flatMap(v => possibleSingleOffsets.map(o => v:+o)))
      cartesian(1, possibleSingleOffsets.map(p => Vector(p)))
    }

    val precomputedMoves = ImmutableList.copyOf(
      possibleOffsets.view.map(new ShiftMove(_)).asJava
        .asInstanceOf[java.lang.Iterable[AggregateMTGDefenderMove]])


    val startingPoints = {
      val r = Range.inclusive(-aggregation/2, aggregation/2+(if(aggregation%2 == 1) 1 else 0))
      r.flatMap(x => r.map(y => config.defenderStartingPositions.map(dp =>  DefenderPosition.cached(dp.x+x, dp.y+y))))
    }.toVector

    val initialStates = startingPoints.map(pos => new MovingTargetsDefenderState(pos, 0, config))

    AggregateMTGDefenderDecisionState(aggregation, initialStates, config, precomputedMoves)
  }
}
