use nebu::Goldilocks;
use crate::noun::{Order, NounId};
use crate::reduce::{reduce, Outcome, ErrorKind, cell_pair, evaluate};
use crate::call::CallProvider;
pub fn call_witness<const N: usize>(
order: &mut Order<N>, object: NounId, body: NounId, budget: u64, calls: &dyn CallProvider<N>,
) -> Outcome {
let (tag_formula, check_formula) = match cell_pair(order, body) {
Some(p) => p,
None => return Outcome::Error(ErrorKind::Malformed),
};
let (tag_result, budget) = match evaluate(order, object, tag_formula, budget, calls) {
Ok(v) => v, Err(o) => return o,
};
let tag_value = match order.atom_value(tag_result) {
Some((v, _)) => v,
None => return Outcome::Error(ErrorKind::TypeError),
};
let witness = match calls.provide(order, tag_value, object) {
Some(w) => w,
None => return Outcome::Halt(budget),
};
let witness_object = match order.cell(witness, object) {
Some(c) => c,
None => return Outcome::Error(ErrorKind::Unavailable),
};
match reduce(order, witness_object, check_formula, budget, calls) {
Outcome::Ok(check_result, budget) => {
match order.atom_value(check_result) {
Some((v, _)) if v == Goldilocks::ZERO => Outcome::Ok(witness, budget),
_ => Outcome::Error(ErrorKind::CallRejected),
}
}
Outcome::Error(_) => Outcome::Error(ErrorKind::CallRejected),
Outcome::Halt(b) => Outcome::Halt(b),
}
}