package pl.edu.pw.mini.sg.game.deterministic.single;

import pl.edu.pw.mini.sg.game.deterministic.{ AdvanceablePlayerVisibleState, PureStrategy }
import scala.annotation.tailrec

import scala.collection.mutable.{Map => MMap}
import scalaz.NonEmptyList

final case class LinearizedAdvanceableGameFifo[M, S <: AdvanceablePlayerVisibleState[M, S]](
  currentHistory: NonEmptyList[S],
  pendingBranches: Vector[NonEmptyList[S]],
  decisions: List[(NonEmptyList[S], M)]
) extends LinearizedGame[M, S, LinearizedAdvanceableGameFifo[M, S]] {
  override val playerState : S = currentHistory.head
  override def possibleMoves: List[M] = playerState.possibleMoves

  override def makeMove(move: M) : LinearizedAdvanceableGameFifo[M, S] = {
    val successorsNF = currentHistory.head.possibleContinuations(move)
    val successors = successorsNF.filter(_.possibleMoves.length > 0)
    val newDecision: List[(NonEmptyList[S], M)] = (currentHistory, move) :: decisions
    val pendingNew = pendingBranches ++ successors.map(_ <:: currentHistory)

    pendingNew match {
      case x+:xs => LinearizedAdvanceableGameFifo(x, xs, newDecision)
      case Vector() => {
        require(successorsNF.head.possibleMoves.isEmpty)
        LinearizedAdvanceableGameFifo(successorsNF.head <:: currentHistory, Vector(), newDecision)
      }
    }
  }

  def pureStrategy : PureStrategy[M, S] = {
    val root = new MutablePureStrategy[M,S](currentHistory.last)
    decisions.foreach{case (histRev, move) => {
      val x = histRev.reverse.tail.foldLeft(root)(_.succ(_))
      x.move = move
      x.moveSet = true
    }}
    root.toImmutable
  }
}


object LinearizedAdvanceableGameFifo extends LinearizedGameProvider {
  override type Game[M, S <: AdvanceablePlayerVisibleState[M, S]] = LinearizedAdvanceableGameFifo[M, S]

  
  override def create[M,S <: AdvanceablePlayerVisibleState[M,S]](state: S) : LinearizedAdvanceableGameFifo[M, S] = new LinearizedAdvanceableGameFifo(NonEmptyList(state), Vector(), Nil)
  override def pureStrategyToLinearizedGameMoveSequence[M, S <: AdvanceablePlayerVisibleState[M, S]](
    root: S,
    pureStrategy : PureStrategy[M, S]
  ) : List[M] = {

    @tailrec
    def genMoves(currentState: S, currentStrategy: PureStrategy[M,S], pendingBranches: Vector[(S, PureStrategy[M, S])], result: List[M]): List[M] = {
      if(currentState.possibleMoves.nonEmpty) {
        val theMove = currentStrategy.move
        val nextStates = currentState.possibleContinuations(theMove).filter(_.possibleMoves.nonEmpty)
        val nextBranches = pendingBranches++nextStates.map(s => (s, currentStrategy.nextState(s)))
        val ll = theMove :: result
        nextBranches match {
          case Vector() => ll
          case (s,str)+:tail => genMoves(s, str, tail, ll)
        }
      } else {
        pendingBranches match {
              case Vector() => result
              case (s,str)+:tail => genMoves(s, str, tail, result)
        }
      }
    }


    genMoves(root, pureStrategy, Vector(), List()).reverse
  }
}
