pub mod host_functions;

use crate::wasm_jumploader::host_functions::HostExternals;
// use genfs::{Fs, OpenOptions};
use wasmi::{ImportsBuilder, ModuleInstance};

pub fn interp() {
    trace!("Interpreting...");
    // let fs = &*FILE_SYSTEM.lock();
    // trace!("Got filesystem");
    // let file = fs
    //     .open(
    //         b"/home/able/bins/aos_test.wasm",
    //         OpenOptions::new().read(true),
    //     )
    //     .unwrap();

    // let mut wasm_binary = Vec::new();

    // let ret = file.read_to_end(&mut wasm_binary).unwrap();
    // trace!("Binary size {}", ret);
    // // Load wasm binary and prepare it for instantiation.
    // let module = wasmi::Module::from_buffer(&wasm_binary).expect("failed to load wasm");
    // trace!("Loaded wasm binary");
    // let imports = ImportsBuilder::new().with_resolver("env", &host_functions::HostExternals {});
    // trace!("Created imports");

    // // Instantiate a module with empty imports and
    // // assert that there is no `start` function.
    // let instance = ModuleInstance::new(&module, &imports); // .expect("failed to instantiate wasm module")

    // match instance {
    //     Ok(inst) => {
    //         let instance = inst.assert_no_start();
    //         let mut is_driver = false;
    //         let _is_program = false;
    //         let mut has_driver_entry = false;
    //         let mut has_driver_exit = false;
    //         let mut has_start = false;

    //         if let Some(_val) = instance.export_by_name("driver_entry") {
    //             has_driver_entry = true;
    //         }

    //         if let Some(_val) = instance.export_by_name("driver_exit") {
    //             has_driver_exit = true;
    //         }

    //         match instance.export_by_name("start") {
    //             Some(_val) => {
    //                 trace!("Program start function found");
    //                 has_start = true;
    //             }
    //             None => debug!("No start function found"),
    //         }

    //         match instance.export_by_name("main") {
    //             Some(_val) => {
    //                 trace!("Program main function found");
    //                 has_start = true;
    //             }
    //             None => debug!("No main function found"),
    //         }

    //         match (has_driver_entry, has_driver_exit) {
    //             (true, true) => {
    //                 trace!("Valid driver entry and exit functions found");
    //                 is_driver = true;
    //             }
    //             (true, false) => error!("Driver entry function found but no driver exit function"),
    //             (false, true) => error!("Driver exit function found but no driver entry function"),
    //             (false, false) => {
    //                 trace!("No driver entry or exit functions found");
    //             }
    //         }

    //         if has_start && has_driver_entry {
    //             error!(
    //                 "A program should not have both a start function and a driver entry function. It Will be treated as a program."
    //             );
    //         }

    //         if has_start {
    //             let ret = instance
    //                 .invoke_export("start", &[], &mut HostExternals {})
    //                 .expect("failed to execute export");

    //             println!("collected wasm return value: {:?}", ret);
    //         } else if is_driver {
    //             let ret = instance
    //                 .invoke_export("driver_entry", &[], &mut HostExternals {})
    //                 .expect("failed to execute export");

    //             println!("collected wasm return value: {:?}", ret);
    //         }
    //     }
    //     Err(err) => error!("{}", err),
    // }
}

pub fn run_program(program: &[u8]) {
    // Load wasm binary and prepare it for instantiation.
    let module = wasmi::Module::from_buffer(program).expect("failed to load wasm");

    trace!("Loaded wasm binary");
    let imports = ImportsBuilder::new().with_resolver("env", &host_functions::HostExternals {});
    trace!("Created imports");

    // Instantiate a module with empty imports and
    // assert that there is no `start` function.
    let instance = ModuleInstance::new(&module, &imports);

    // .expect("failed to instantiate wasm module")

    match instance {
        Ok(inst) => {
            let instance = inst.assert_no_start();
            let mut is_driver = false;
            let _is_program = false;
            let mut has_driver_entry = false;
            let mut has_driver_exit = false;
            let mut has_start = false;

            if let Some(_val) = instance.export_by_name("driver_entry") {
                has_driver_entry = true;
            }

            if let Some(_val) = instance.export_by_name("driver_exit") {
                has_driver_exit = true;
            }

            match instance.export_by_name("start") {
                Some(_val) => {
                    trace!("Program start function found");
                    has_start = true;
                }
                None => debug!("No start function found"),
            }

            match instance.export_by_name("main") {
                Some(_val) => {
                    trace!("Program main function found");
                    has_start = false;
                }
                None => debug!("No main function found"),
            }

            match (has_driver_entry, has_driver_exit) {
                (true, true) => {
                    trace!("Valid driver entry and exit functions found");
                    is_driver = true;
                }
                (true, false) => error!("Driver entry function found but no driver exit function"),
                (false, true) => error!("Driver exit function found but no driver entry function"),
                (false, false) => {
                    trace!("No driver entry or exit functions found");
                }
            }

            if has_start && has_driver_entry {
                error!(
                    "A program should not have both a start function and a driver entry function. It Will be treated as a program."
                );
            }

            if has_start {
                let ret = instance
                    .invoke_export("start", &[], &mut HostExternals {})
                    .expect("failed to execute export");

                println!("collected wasm return value: {:?}", ret);
            } else if is_driver {
                let ret = instance
                    .invoke_export("driver_entry", &[], &mut HostExternals {})
                    .expect("failed to execute export");

                println!("collected wasm return value: {:?}", ret);
            } else {
                println!("ERROR: No start or driver entry function found, invalid program");
            }
        }
        Err(err) => error!("{}", err),
    }
}
