use core::fmt::Error;

// use crate::aalloc::aalloc;
use crate::arch::interrupts::{reset_pit_for_cpu, set_pit_2};
use crate::arch::{generate_process_pass, shutdown};
use crate::channels::{Channel, ChannelPermission};
use crate::devices::pci;
use crate::filesystem::FILE_SYSTEM;
use crate::handle::Handle;
use crate::ipc::IPC;
use crate::rhai_shell::shell;
use crate::rhai_shell::KEYBUFF;
use crate::vterm::Term;
use crate::wasm_jumploader::run_program;
use crate::{vgai, SCREEN_BUFFER};
use acpi::{AcpiTables, PlatformInfo};
use alloc::collections::{vec_deque, VecDeque};
use cpuio::inb;
use cpuio::outb;
use ext2::fs::sync::{DirectoryEntry, Synced};
use ext2::fs::Ext2;
use ext2::sector::Size1024;
use ext2::sys::inode;
use genfs::OpenOptions;
use genfs::{DirEntry, Fs};
use kernel::proccess::PID;
use kernel::software_int;
use spin::Lazy;
use vga::writers::GraphicsWriter;

// TODO: move to a better place
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AcpiStruct {}
impl acpi::AcpiHandler for AcpiStruct {
    unsafe fn map_physical_region<T>(
        &self,
        physical_address: usize,
        size: usize,
    ) -> acpi::PhysicalMapping<Self, T> {
        info!("PHYS ADDR: {:?}", physical_address);
        info!("Size: {:?}", size);
        todo!("map_physical_region");
    }
    fn unmap_physical_region<T>(_region: &acpi::PhysicalMapping<Self, T>) {
        todo!("unmap_physical_region");
    }
}

pub static TERM: Lazy<spin::Mutex<Term>> = Lazy::new(|| spin::Mutex::new(Term::new()));
#[derive(Debug)]

pub struct Path {
    pub path: Vec<String>,
}

impl Path {
    pub fn new(path: String) -> Self {
        let mut path_vec_string = vec![];

        for part in path.split(&['\\', '/'][..]) {
            path_vec_string.push(part.to_string());
        }

        Path {
            path: path_vec_string,
        }
    }
}

/// Experimental scratchpad for testing.
pub fn scratchpad() {
    real_shell();
}

pub fn acpi() {
    let acpi_handler = AcpiStruct {};
    let _table;
    unsafe {
        _table = AcpiTables::search_for_rsdp_bios(acpi_handler);
    }
    match _table.unwrap().platform_info().unwrap() {
        PlatformInfo {
            power_profile,
            interrupt_model,
            ..
        } => {
            info!("{:?}", power_profile);
            info!("{:?}", interrupt_model);
            // info!("{:?}", processor_info.unwrap());
            // info!("{:?}", pm_timer.unwrap());
        }
    }
}

pub fn real_shell() {
    let _current_dir = "/".to_string();
    let current_user = "able".to_string();

    let mut buf = String::new();
    print!("> ");

    loop {
        match x86_64::instructions::interrupts::without_interrupts(|| KEYBUFF.lock().pop()) {
            Some('\n') => {
                // match engine.eval_with_scope::<rhai::Dynamic>(&mut scope, &buf) {
                // Ok(o) => println!("{o}"),

                // Err(e) => println!("Eval error: {e}"),
                // };
                if !buf.is_empty() {
                    command_parser(current_user.clone(), buf.clone());
                }

                buf.clear();
                print!("> ");
            }
            Some('\u{0008}') => {
                buf.pop();
            }

            Some('\u{0009}') => {
                buf.push(' ');
                buf.push(' ');
                buf.push(' ');
                buf.push(' ');
            }

            Some(chr) => buf.push(chr),
            None => (),
        }
    }
}

pub fn command_parser(user: String, command: String) {
    let fs = &*FILE_SYSTEM.lock();
    let mut iter = command.split_whitespace();

    // TODO: update the open() function to take either a ableOS path or a b"/" type path
    let current_path = Path::new("/home/able".to_string());
    trace!("Current path: {:?}", current_path);
    let current_path = b"/home/able/";

    let bin_name = iter.next().unwrap();

    match bin_name {
        "rhai" => {
            drop(fs);
            shell();
        }
        "list" | "ls" => {
            for dir_entry in list_files_in_dir(fs, current_path) {
                println!("{}", dir_entry.file_name_string());
            }
        }
        "quit" => shutdown(),

        _ => {
            let home_exec_path = format!("/home/{}/bins/{}.wasm", user, bin_name);
            let shared_exec_path = format!("/shared/bins/{}.wasm", bin_name);
            let system_exec_path = format!("/system/bins/{}.wasm", bin_name);

            let home_exec_file = fs.open(&home_exec_path.as_bytes(), OpenOptions::new().read(true));
            let shared_exec_file =
                fs.open(&shared_exec_path.as_bytes(), OpenOptions::new().read(true));
            let system_exec_file =
                fs.open(&system_exec_path.as_bytes(), OpenOptions::new().read(true));

            let mut in_home = false;
            let mut in_shared = false;
            let mut in_system = false;

            let mut binary_prog: Vec<u8> = vec![];

            match home_exec_file {
                Ok(file) => {
                    let ret = file.read_to_end(&mut binary_prog).unwrap();
                    in_home = true;
                }

                Err(error) => {
                    trace!("{:?}", error);
                    in_home = false;
                }
            }
            match shared_exec_file {
                Ok(file) => {
                    let ret = file.read_to_end(&mut binary_prog).unwrap();
                    in_shared = true;
                }
                Err(error) => {
                    trace!("{:?}", error);
                    in_shared = false;
                }
            }
            match system_exec_file {
                Ok(file) => {
                    let ret = file.read_to_end(&mut binary_prog).unwrap();
                    in_system = true;
                }

                Err(error) => {
                    trace!("{:?}", error);
                    in_system = false;
                }
            }

            let args = iter.collect::<Vec<&str>>();
            println!("{:?}", args);
            if in_home || in_shared || in_system {
                run_program(&binary_prog);
            } else {
                println!("No such binary: {}", bin_name);
                error!("No such binary: {}", bin_name);
            }
        }
    }
}

pub fn sound(n_frequency: u32) {
    let div: u32;
    let tmp: u8;

    div = 1193180 / n_frequency;
    unsafe {
        outb(0xb6, 0x43);

        set_pit_2(div);

        //And play the sound using the PC speaker
        tmp = inb(0x61);
        if tmp != (tmp | 3) {
            outb(tmp | 3, 0x61);
        }
    }
}

pub fn sound_off() {
    unsafe {
        let tmp = inb(0x61) & 0xFC;
        outb(tmp, 0x61)
    };
    reset_pit_for_cpu();
}

pub fn list_files_in_dir(fs: &Synced<Ext2<Size1024, Vec<u8>>>, path: &[u8]) -> Vec<DirectoryEntry> {
    let mut entry_list = vec![];

    let dirr = fs.read_dir(b"/").unwrap();
    for dir_entry in dirr {
        entry_list.push(dir_entry.unwrap());
    }

    entry_list
}
