// ============================================================
// Threshold parallel policy
// ============================================================

///|
/// Tick all children until at least `successes_needed` succeed.
/// A quorum can fail early when the remaining children cannot reach it.
pub fn quorum_node(children : Array[Node], successes_needed : Int) -> Node {
  let required = if successes_needed > 0 { successes_needed } else { 1 }
  let tick = fn(bb) {
    let mut success_count = 0
    let mut failure_count = 0
    let mut running_count = 0
    for child in children {
      match child.tick(bb) {
        Status::BTSuccess => success_count = success_count + 1
        Status::BTFailure => failure_count = failure_count + 1
        Status::BTRunning => running_count = running_count + 1
      }
    }
    if success_count >= required {
      for child in children {
        child.reset()
      }
      Status::BTSuccess
    } else if success_count + running_count < required {
      for child in children {
        child.reset()
      }
      Status::BTFailure
    } else {
      Status::BTRunning
    }
  }
  let reset = fn() {
    for child in children {
      child.reset()
    }
  }
  Node::new(tick, reset)
}

///|
/// Return the number of child outcomes that are already terminal.
pub fn count_terminal(children : Array[Node], bb : Blackboard) -> Int {
  let mut count = 0
  for child in children {
    match child.tick(bb) {
      Status::BTSuccess | Status::BTFailure => count = count + 1
      Status::BTRunning => ()
    }
  }
  count
}

///|
/// Select the first child that succeeds, resetting a running loser on success.
pub fn race_node(children : Array[Node]) -> Node {
  let tick = fn(bb) {
    let mut running_count = 0
    for child in children {
      match child.tick(bb) {
        Status::BTSuccess => {
          for other in children {
            other.reset()
          }
          return Status::BTSuccess
        }
        Status::BTFailure => ()
        Status::BTRunning => running_count = running_count + 1
      }
    }
    if running_count > 0 {
      Status::BTRunning
    } else {
      Status::BTFailure
    }
  }
  let reset = fn() {
    for child in children {
      child.reset()
    }
  }
  Node::new(tick, reset)
}