//! EmitJoinIRStepBox (Phase 106)
//!
//! Responsibility: call Pattern2 JoinIR lowerer and build inline boundary.

use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;

use super::super::pattern2_inputs_facts_box::{Pattern2DebugLog, Pattern2Inputs};
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;

use std::collections::BTreeMap;

pub(crate) struct EmitJoinIRStepOutput {
    pub join_module: crate::mir::join_ir::JoinModule,
    pub boundary: JoinInlineBoundary,
}

pub(crate) struct EmitJoinIRStepBox;

impl EmitJoinIRStepBox {
    pub(crate) fn emit(
        builder: &mut MirBuilder,
        condition: &ASTNode,
        body_ast: &[ASTNode],
        effective_break_condition: &ASTNode,
        carrier_updates: &BTreeMap<String, crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr>,
        inputs: &mut Pattern2Inputs,
        debug: bool,
        verbose: bool,
        skeleton: Option<&crate::mir::loop_canonicalizer::LoopSkeleton>,
    ) -> Result<EmitJoinIRStepOutput, String> {
        use crate::mir::join_ir::lowering::loop_with_break_minimal::lower_loop_with_break_minimal;

        let log = Pattern2DebugLog::new(verbose);

        let lowering_inputs = crate::mir::join_ir::lowering::loop_with_break_minimal::LoopWithBreakLoweringInputs {
            scope: inputs.scope.clone(),
            condition,
            break_condition: effective_break_condition,
            env: &inputs.env,
            carrier_info: &inputs.carrier_info,
            carrier_updates,
            body_ast,
            body_local_env: Some(&mut inputs.body_local_env),
            allowed_body_locals_for_conditions: if inputs.allowed_body_locals_for_conditions.is_empty() {
                None
            } else {
                Some(inputs.allowed_body_locals_for_conditions.as_slice())
            },
            join_value_space: &mut inputs.join_value_space,
            skeleton,
            condition_only_recipe: inputs.condition_only_recipe.as_ref(),
            body_local_derived_recipe: inputs.body_local_derived_recipe.as_ref(),
            balanced_depth_scan_recipe: inputs.balanced_depth_scan_recipe.as_ref(),
        };

        let (join_module, fragment_meta) = match lower_loop_with_break_minimal(lowering_inputs) {
            Ok((module, meta)) => (module, meta),
            Err(e) => {
                super::super::super::trace::trace().debug("pattern2", &format!("Pattern 2 lowerer failed: {}", e));
                return Err(format!("[cf_loop/pattern2] Lowering failed: {}", e));
            }
        };

        let exit_meta = &fragment_meta.exit_meta;
        use crate::mir::builder::control_flow::joinir::merge::exit_line::ExitMetaCollector;
        let exit_bindings = ExitMetaCollector::collect(builder, exit_meta, Some(&inputs.carrier_info), debug);

        // JoinIR main() params: [ValueId(0), ValueId(1), ...]
        let mut join_input_slots = vec![ValueId(0)];
        let mut host_input_values = vec![inputs.loop_var_id];
        for (idx, carrier) in inputs.carrier_info.carriers.iter().enumerate() {
            join_input_slots.push(ValueId((idx + 1) as u32));
            host_input_values.push(carrier.host_id);
        }

        use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
        let boundary = JoinInlineBoundaryBuilder::new()
            .with_inputs(join_input_slots, host_input_values)
            .with_condition_bindings(std::mem::take(&mut inputs.condition_bindings))
            .with_exit_bindings(exit_bindings.clone())
            .with_expr_result(fragment_meta.expr_result)
            .with_loop_var_name(Some(inputs.loop_var_name.clone()))
            .with_carrier_info(inputs.carrier_info.clone())
            .build();

        log.log("emit", "JoinIR module + boundary constructed");

        Ok(EmitJoinIRStepOutput { join_module, boundary })
    }
}
