//
//  Copyright 2025-2026 Shuntaro Kasatani
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

pub mod assert;
pub mod builders;

use kasl_core::{
    ast::{
        CompilationData,
        compilation_data::{CompilerState, ProgramContext},
        scope_manager::IOBlueprint,
    },
    ast_construction::{
        blueprint_builder::BlueprintBuilder, flow_graph_analyzing::FlowGraphAnalyzer,
        global_decl_collection::GlobalDeclCollector, scope_graph_analyzing::ScopeGraphAnalyzer,
        statement_building::StatementBuilder,
    },
    builtin::BuiltinRegistry,
    error::{ErrorCollector, ErrorRecord},
    lowerer::Lowerer,
    parser::{ParserDeclStmt, kasl_parser},
};
use kasl_cranelift_backend::CraneliftBackend;

pub struct TestContext {
    pub ec: ErrorCollector,
    pub prog_ctx: ProgramContext,
    pub comp_data: CompilationData,
    pub builtin_registry: BuiltinRegistry,
    pub comp_state: CompilerState,
}

impl Default for TestContext {
    fn default() -> Self {
        let mut test_ctx = Self {
            ec: ErrorCollector::default(),
            prog_ctx: ProgramContext::default(),
            comp_data: CompilationData::default(),
            builtin_registry: BuiltinRegistry::default(),
            comp_state: CompilerState::default(),
        };
        let root_namespace_id = test_ctx.prog_ctx.namespace_registry.get_root_namespace_id();
        test_ctx
            .prog_ctx
            .scope_registry
            .create_global_scope(root_namespace_id);
        test_ctx
    }
}

pub fn parse_expr(input: &str) -> Vec<ParserDeclStmt> {
    kasl_parser::parse(input).unwrap()
}

pub fn collect_global_decls(
    test_ctx: &mut TestContext,
    statements: &[ParserDeclStmt],
) -> Result<(), Vec<ErrorRecord>> {
    let root_namespace_id = test_ctx.prog_ctx.namespace_registry.get_root_namespace_id();
    let mut global_decl_collector = GlobalDeclCollector::new(
        &mut test_ctx.ec,
        &mut test_ctx.prog_ctx,
        &mut test_ctx.comp_data,
        &test_ctx.comp_state,
        &test_ctx.builtin_registry,
        root_namespace_id,
    );
    global_decl_collector.process(statements);
    test_ctx.ec.as_result().map(|_| ())
}

pub fn build_stmts(test_ctx: &mut TestContext) -> Result<(), Vec<ErrorRecord>> {
    let mut stmt_builder = StatementBuilder::new(
        &mut test_ctx.ec,
        &mut test_ctx.prog_ctx,
        &mut test_ctx.comp_data,
        &test_ctx.builtin_registry,
    );
    stmt_builder.build_all();
    test_ctx.ec.as_result().map(|_| ())
}

pub fn analyze_scopes(test_ctx: &mut TestContext) -> Result<(), Vec<ErrorRecord>> {
    let mut scope_graph_analyzer = ScopeGraphAnalyzer::new(
        &mut test_ctx.ec,
        &test_ctx.prog_ctx,
        &mut test_ctx.comp_data.scope_graph,
    );
    scope_graph_analyzer.analyze_all();
    test_ctx.ec.as_result().map(|_| ())
}

pub fn analyze_flow_graph(test_ctx: &mut TestContext) -> Result<(), Vec<ErrorRecord>> {
    let mut flow_graph_analyzer = FlowGraphAnalyzer::new(
        &mut test_ctx.ec,
        &test_ctx.prog_ctx,
        &test_ctx.comp_data.func_flow_graphs,
        &test_ctx.comp_data.op_flow_graphs,
    );
    flow_graph_analyzer.analyze_all();
    test_ctx.ec.as_result().map(|_| ())
}

pub fn build_blueprint(test_ctx: &mut TestContext) -> IOBlueprint {
    let blueprint_builder = BlueprintBuilder::new(&test_ctx.prog_ctx);
    blueprint_builder.build()
}

pub fn execute_program(
    test_ctx: &mut TestContext,
    blueprint: &IOBlueprint,
    inputs: &[*mut ()],
    outputs: &[*mut ()],
    states: &[*mut ()],
) {
    // Lower the function to KASL-IR
    let lowerer = Lowerer;
    let root_namespace_id = test_ctx.prog_ctx.namespace_registry.get_root_namespace_id();
    let main_func_id = test_ctx
        .prog_ctx
        .func_ctx
        .get_global_func_id(root_namespace_id, "main")
        .unwrap();
    let ir_func = lowerer.lower_once(
        &test_ctx.prog_ctx,
        &test_ctx.builtin_registry,
        blueprint,
        &main_func_id,
    );

    // Compile the KASL-IR function to native program
    let mut backend = CraneliftBackend::default();
    let code = backend.compile(ir_func).unwrap();

    unsafe {
        run_code(code, inputs.as_ptr(), outputs.as_ptr(), states.as_ptr());
    }
}

unsafe fn run_code(
    code_ptr: *const u8,
    input: *const *mut (),
    output: *const *mut (),
    state: *const *mut (),
) {
    unsafe {
        let code_fn: fn(*const *mut (), *const *mut (), *const *mut ()) =
            std::mem::transmute(code_ptr);
        code_fn(input, output, state)
    }
}
