package pl.edu.pw.mini.sg.bosansky

import pl.edu.pw.mini.sg.game.deterministic.{AdvanceablePlayerVisibleState, DeterministicGame, PlayerVisibleState}
import pl.edu.pw.mini.sg.util.StreamUtils
import scala.annotation.tailrec

class DeterministicGameUtilsProvider[DM, AM, DS <: PlayerVisibleState[DM], AS <: AdvanceablePlayerVisibleState[AM, AS], G <: DeterministicGame[DM, AM, DS, AS, G]] extends GameUtilsProvider[DM, AM, DS, AS, G] {

  override def getAllSequences(initialState: G): Stream[(G, List[ExtendedMove[DM, DS]], List[ExtendedMove[AM, AS]])] = {
    def recSequences(stack: List[(G, List[ExtendedMove[DM, DS]], List[ExtendedMove[AM, AS]])]) : Stream[(G, List[ExtendedMove[DM, DS]], List[ExtendedMove[AM, AS]])] = {
      if(stack.isEmpty)
        Stream.empty
      else {
        val (currentState, currentDefenderMoves, currentAttackerMoves) :: tail = stack
        val newNodes = for (defenderMove <- currentState.defenderState.possibleMoves; attackerMove <- currentState.attackerState.possibleMoves)
          yield (currentState.makeMove(defenderMove, attackerMove),
            ExtendedMove(defenderMove, currentState.defenderState)::currentDefenderMoves,
            ExtendedMove(attackerMove, currentState.attackerState)::currentAttackerMoves)
        (currentState, currentDefenderMoves, currentAttackerMoves) #:: recSequences(newNodes ++ tail)
      }
    }

    recSequences(List((initialState, List(), List())))
  }

  @tailrec
  final override def getNodeFromSequences(initialState: G, defenderSequence: List[ExtendedMove[DM, DS]], attackerSequence: List[ExtendedMove[AM, AS]]): Option[G] = {
    (attackerSequence, defenderSequence) match {
      case (Nil, Nil) => Some(initialState)
      case (a::as, d::ds) if initialState.attackerState.possibleMoves.contains(a.move) && initialState.defenderState.possibleMoves.contains(d.move) => getNodeFromSequences(initialState.makeMove(d.move, a.move), ds, as)
      case _ => None
    }
  }

  override def getAttackerInformationSets(sequences: => Stream[(G, List[ExtendedMove[DM, DS]], List[ExtendedMove[AM, AS]])]): Set[InformationSetId[AM, AS]] = {
    StreamUtils.streamDistinct(sequences.map(s => InformationSetId(s._3, s._1.attackerState)))
  }

  override def getDefenderInformationSets(sequences: => Stream[(G, List[ExtendedMove[DM, DS]], List[ExtendedMove[AM, AS]])]): Set[InformationSetId[DM, DS]] = {
    StreamUtils.streamDistinct(sequences.map(s => InformationSetId(s._2, s._1.defenderState)))
  }
}
