// ============================================================
// Runtime lifecycle helpers
//
// TreeRunner is the small integration boundary recommended for applications.
// It owns one tree and one blackboard, exposes explicit reset semantics, and
// keeps the frame counter needed by game loops, services, and telemetry.
// ============================================================

///|
pub struct TreeRunner {
  root : Node
  bb : Blackboard
  frame : Ref[Int]
  last : Ref[Status?]
}

///|
/// Create a runner with an isolated blackboard.
pub fn TreeRunner::new(root : Node, bb : Blackboard) -> TreeRunner {
  { root, bb, frame: Ref::new(0), last: Ref::new(None) }
}

///|
/// Tick one frame and record the last observed status.
pub fn TreeRunner::tick(self : TreeRunner) -> Status {
  let status = self.root.tick(self.bb)
  self.frame.set(self.frame.get() + 1)
  self.last.set(Some(status))
  status
}

///|
/// Run at most `max_ticks` frames, returning early on a terminal status.
pub fn TreeRunner::run_until_terminal(
  self : TreeRunner,
  max_ticks : Int,
) -> Status {
  let mut i = 0
  let mut status = self.last.get().unwrap_or(Status::BTRunning)
  while i < max_ticks {
    status = self.tick()
    i = i + 1
    match status {
      Status::BTRunning => ()
      _ => return status
    }
  }
  status
}

///|
/// Reset the tree and frame-local lifecycle state.
pub fn TreeRunner::reset(self : TreeRunner) -> Unit {
  self.root.reset()
  self.frame.set(0)
  self.last.set(None)
}

///|
/// Return the number of frames ticked since the last reset.
pub fn TreeRunner::frame(self : TreeRunner) -> Int {
  self.frame.get()
}

///|
/// Return the most recent status, if the runner has been ticked.
pub fn TreeRunner::last_status(self : TreeRunner) -> Status? {
  self.last.get()
}

///|
/// Return the runner's isolated blackboard.
pub fn TreeRunner::blackboard(self : TreeRunner) -> Blackboard {
  self.bb
}

///|
/// Return whether the last tick completed.
pub fn TreeRunner::is_terminal(self : TreeRunner) -> Bool {
  match self.last.get() {
    Some(Status::BTSuccess | Status::BTFailure) => true
    _ => false
  }
}

///|
/// Tick a tree a fixed number of times. Useful for deterministic simulations.
pub fn tick_for_frames(
  root : Node,
  bb : Blackboard,
  frames : Int,
) -> Array[Status] {
  let results : Array[Status] = []
  let mut i = 0
  while i < frames {
    results.push(root.tick(bb))
    i = i + 1
  }
  results
}