package pl.edu.pw.mini.jk.sg.uct.tree;

import java.util.{List, Collection}
import java.util.function.Function

import org.apache.commons.math3.random.RandomGenerator
import pl.edu.pw.mini.jk.sg.uct.{UCTDecisionStatistics, SelectionFunction}
import pl.edu.pw.mini.jk.sg.uct.tree.export.ExportUctTree

trait UctNode[S, M] {
  def createEmptyTree: UctNode[S, M]
  def getState: S
  def getBestMove(rng: RandomGenerator): M;
  def getSuccessor(move: M): Option[UctNode[S, M]]
  def getStatistics: Collection[UctMoveStatistics[M]]
  def addSuccessor(move: M, state: S, possibleMoves: List[M]): UctNode[S, M]
  def selectMove(fun: SelectionFunction): Option[M]
  def selectOrRandomUnvisidedMove(fun: SelectionFunction, rng: RandomGenerator) : M

  def getRealVisitsCount: Int
  def getVisitsCount: Int
  def registerVisit(move: M): Unit
  def registerPayoff(move: M, payoff: Double): Unit
  def moveCount: Int

  //TODO: wywoływanie tych elementów powinno być tylko na korzeniu.
  // docelowo: powinien być oddzielny interfejs "drzewo", a oddzielny "węzeł"
  def reduceStatistics(reduceFunction: Function[UCTDecisionStatistics, UCTDecisionStatistics]) : UctNode[S, M]
  def reduceStatistics(reduceFunction: UCTDecisionStatistics => UCTDecisionStatistics): UctNode[S, M]
  def allStatistics: Stream[UnmodifiableDecisionStatisticsView]
  def pruneTree(keepPredicate: PruneTreeFunctions): UctNode[S, M]
  def transformStatistics(payoffTransformer: PayoffTransformer): UctNode[S, M]
  def exportTree: ExportUctTree[S, M]
}
