// ============================================================
// Composable condition helpers
// ============================================================

///|
/// Evaluate all predicates from left to right.
pub fn all_conditions(
  predicates : Array[(Blackboard) -> Bool],
  bb : Blackboard,
) -> Bool {
  for predicate in predicates {
    if !predicate(bb) {
      return false
    }
  }
  true
}

///|
/// Evaluate predicates until one is true.
pub fn any_conditions(
  predicates : Array[(Blackboard) -> Bool],
  bb : Blackboard,
) -> Bool {
  for predicate in predicates {
    if predicate(bb) {
      return true
    }
  }
  false
}

///|
/// Create the logical negation of a predicate.
pub fn not_condition(predicate : (Blackboard) -> Bool) -> (Blackboard) -> Bool {
  fn(bb : Blackboard) { !predicate(bb) }
}

///|
/// A condition node backed by a set of predicates.
pub fn all_condition_node(predicates : Array[(Blackboard) -> Bool]) -> Node {
  condition_node(fn(bb : Blackboard) { all_conditions(predicates, bb) })
}

///|
/// A condition node that succeeds when any predicate is true.
pub fn any_condition_node(predicates : Array[(Blackboard) -> Bool]) -> Node {
  condition_node(fn(bb : Blackboard) { any_conditions(predicates, bb) })
}

///|
/// Compare a blackboard integer with a threshold.
pub(all) enum IntComparison {
  LessThan
  LessOrEqual
  EqualTo
  GreaterOrEqual
  GreaterThan
} derive(Eq, Debug)

///|
/// Return every comparison in a stable order for configuration UIs.
pub fn all_int_comparisons() -> Array[IntComparison] {
  [
    IntComparison::LessThan,
    IntComparison::LessOrEqual,
    IntComparison::EqualTo,
    IntComparison::GreaterOrEqual,
    IntComparison::GreaterThan,
  ]
}

///|
/// Return whether an integer field satisfies a comparison.
pub fn compare_int(
  bb : Blackboard,
  key : String,
  comparison : IntComparison,
  expected : Int,
) -> Bool {
  match bb.get_int(key) {
    None => false
    Some(actual) =>
      match comparison {
        IntComparison::LessThan => actual < expected
        IntComparison::LessOrEqual => actual <= expected
        IntComparison::EqualTo => actual == expected
        IntComparison::GreaterOrEqual => actual >= expected
        IntComparison::GreaterThan => actual > expected
      }
  }
}

///|
/// Make an integer comparison node.
pub fn int_comparison_node(
  key : String,
  comparison : IntComparison,
  expected : Int,
) -> Node {
  condition_node(fn(bb : Blackboard) {
    compare_int(bb, key, comparison, expected)
  })
}

///|
/// Detect whether an integer value changed since the previous tick.
pub fn int_changed_node(key : String) -> Node {
  let previous : Ref[Int?] = Ref::new(None)
  let tick = fn(bb : Blackboard) {
    match bb.get_int(key) {
      None => Status::BTFailure
      Some(value) =>
        match previous.get() {
          None => {
            previous.set(Some(value))
            Status::BTFailure
          }
          Some(old) => {
            previous.set(Some(value))
            if old == value {
              Status::BTFailure
            } else {
              Status::BTSuccess
            }
          }
        }
    }
  }
  let reset = fn() { previous.set(None) }
  Node::new(tick, reset)
}