#![allow(clippy::empty_loop)]

use acpi::AcpiTables;
use x86_64::instructions::interrupts::{disable, enable};

use crate::scratchpad;

use {
    crate::{
        arch::{init, sloop},
        boot_conf,
        boot_conf::BootConfig,
        capabilities::FileAccess,
        experiments::{
            info::master,
            systeminfo::{KERNEL_VERSION, RELEASE_TYPE},
        },
        file::PathRep,
        relib::network::socket::{SimpleSock, Socket},
        scheduler::SCHEDULER,
        VgaBuffer, SCREEN_BUFFER,
    },
    alloc::{
        format,
        string::{String, ToString},
        vec,
    },
    core::sync::atomic::{AtomicU64, Ordering::*},
    facepalm::start_facepalm,
    lazy_static::lazy_static,
    log::*,
    shadeable::pixel_format::from_vga_16,
    vga::colors::Color16,
};

lazy_static! {
    // TODO: Change this structure to allow for multiple cores loaded
    pub static ref TICK: AtomicU64 = AtomicU64::new(0);
    pub static ref BOOT_CONF: BootConfig = boot_conf::BootConfig::new();
}

/// The main entry point of the kernel
#[no_mangle]
pub fn kernel_main() -> ! {
    init::init();
    log::set_max_level(BOOT_CONF.log_level());

    let mut scheduler = SCHEDULER.lock();

    use crate::scheduler::Priority::*;
    let mut process_1 = scheduler.new_process(High);
    process_1.capabilities.files = FileAccess::Some(vec![PathRep {
        location: FileLocations::Home,
        file_name: "test".to_string(),
    }]);
    scheduler.add_process(process_1);
    for ref_process in &scheduler.list {
        trace!("{:?}", ref_process);
    }
    drop(scheduler);

    use crate::proto_filetable::file::FileLocations;

    if false {
        let mut sock_print_id = SimpleSock::new();
        sock_print_id.register_protocol("Screen Printer".to_string());
        sock_print_id.write(format!("🐑").into());

        let mut mode = SCREEN_BUFFER.lock();

        mode.force_redraw();
        for current in (*String::from_utf8_lossy(&sock_print_id.peek().unwrap())).chars() {
            mode.draw_char(0, 0, current, from_vga_16(Color16::Red));
        }
        mode.copy_to_buffer();
    }

    // TODO: create a scratchpad module
    if false {
        // Currently not implemented
        let acpi_handler = AcpiStruct {};
        let mut table;
        unsafe {
            table = AcpiTables::search_for_rsdp_bios(acpi_handler);
        }
    }

    start_facepalm();
    scratchpad();

    if false {
        disable();
        let mut mode = SCREEN_BUFFER.lock();
        mode.force_redraw();

        mode.copy_to_buffer();
        mode.clear();

        mode.draw_char(0, 0, 'v', 0xff00ffff);

        mode.copy_to_buffer();
        drop(mode);
        enable()
        // sloop::halt();
    }

    sloop()
}

/// called by arch specific timers to tick up all kernel related functions
pub fn tick() {
    let mut data = TICK.load(Relaxed);
    data += 1;

    crate::kernel_state::KERNEL_STATE.lock().update_state();
    // let mut scheduler = SCHEDULER.lock();
    // scheduler.bump_exec();
    // drop(scheduler);

    TICK.store(data, Relaxed)
}

pub fn cpu_socket_startup() {
    let mut cpu_info_socket = SimpleSock::new();
    cpu_info_socket.register_protocol("CPU_INFO".to_string());

    let x = master().unwrap();
    let _xyz = x.brand_string().unwrap();
}

pub fn log_version_data() {
    info!("{} v{}", RELEASE_TYPE, KERNEL_VERSION);
    info!(
        "Brand String: {}",
        master().unwrap().brand_string().unwrap()
    );
}
// TODO: Split up into the randomness and the password generation
pub fn generate_process_pass() -> u128 {
    // TODO: Move this into entropy_pool module
    use rdrand::RdRand;
    let gen = RdRand::new().unwrap();

    // TODO: Split off into process module
    let ret = (gen.try_next_u64().unwrap() as u128) << 64 | (gen.try_next_u64().unwrap() as u128);
    ret
}

// 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");
    }
}