// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT.
/*
 *  Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *  SPDX-License-Identifier: Apache-2.0
 */

//TODO(bdd): Should this just be an [i32; 3]? Might make it easier to make things const?
/// Binary Decision Diagram node representation
#[derive(Debug, Clone, Copy)]
pub(crate) struct BddNode {
    pub condition_index: i32,
    pub high_ref: i32,
    pub low_ref: i32,
}

#[allow(dead_code)]
const RESULT_LIMIT: i32 = 100_000_000;
#[allow(dead_code)]
const TERMINAL_TRUE: i32 = 1;
#[allow(dead_code)]
const TERMINAL_FALSE: i32 = -1;

/// Evaluates a BDD to resolve an endpoint result
///
/// Arguments
/// * `nodes` - Array of BddNodes
/// * `conditions` - Array of conditions referenced by nodes
/// * `root_ref` - Root reference to start evaluation
/// * `params` - Parameters for condition evaluation
/// * `context` - Values that can be set/mutated by the conditions
/// * `diagnostic_collector` - a struct for collecting information about the execution of conditions
/// * `condition_evaluator` - Function to evaluate individual conditions with params and context
/// * `result_builder` - Function to construct the endpoint result from a result index
///
/// Returns
/// * `Some(R)` - Result if evaluation succeeds
/// * `None` - No match found
#[allow(clippy::too_many_arguments, dead_code)]
pub(crate) fn evaluate_bdd<'a, Cond, Params, R, Context>(
    nodes: &[BddNode],
    conditions: &[Cond],
    root_ref: i32,
    params: &'a Params,
    context: &mut Context,
    diagnostic_collector: &mut crate::endpoint_lib::diagnostic::DiagnosticCollector,
    mut condition_evaluator: impl FnMut(&Cond, &'a Params, &mut Context, &mut crate::endpoint_lib::diagnostic::DiagnosticCollector) -> bool,
    result_builder: impl FnOnce(usize, &'a Params, &Context) -> R,
) -> Option<R> {
    let mut current_ref = root_ref;

    loop {
        match current_ref {
            // Result references (>= 100_000_000)
            ref_val if ref_val >= RESULT_LIMIT => {
                let result_index = (ref_val - RESULT_LIMIT) as usize;
                return Some(result_builder(result_index, params, context));
            }
            // Terminals (1 = TRUE, -1 = FALSE) NoMatchRule
            TERMINAL_TRUE | TERMINAL_FALSE => {
                return Some(result_builder(0, params, context));
            }
            // Node references
            ref_val => {
                let is_complement = ref_val < 0;
                let node_index = (ref_val.abs() - 1) as usize;
                let node = nodes.get(node_index)?;
                let condition_index = node.condition_index as usize;
                let condition = conditions.get(condition_index)?;
                let condition_result = condition_evaluator(condition, params, context, diagnostic_collector);
                // Handle complement edges: complement inverts the branch selection
                current_ref = if is_complement ^ condition_result { node.high_ref } else { node.low_ref };
            }
        }
    }
}
