pub mod memory;

mod gdt;
mod interrupts;
mod logging;

pub use logging::log;
pub use memory::PAGE_SIZE;

use crate::allocator;
use limine::{HhdmRequest, KernelFileRequest, MemmapRequest, ModuleRequest};
use x86_64::VirtAddr;

extern "C" {
    fn _initial_kernel_heap_start();
    fn _initial_kernel_heap_size();
}

const INITIAL_KERNEL_HEAP_START: *mut u8 = _initial_kernel_heap_start as _;
const INITIAL_KERNEL_HEAP_SIZE: *const () = _initial_kernel_heap_size as _;

#[no_mangle]
unsafe extern "C" fn _kernel_start() -> ! {
    logging::init();
    crate::logger::init().expect("failed to set logger");
    log::info!("Initialising AKern {}", crate::VERSION);

    static HDHM_REQ: HhdmRequest = HhdmRequest::new(0);
    memory::init_pt(VirtAddr::new(
        HDHM_REQ
            .get_response()
            .get()
            .expect("tried to get physical memory mapping offset from Limine")
            .offset,
    ));

    allocator::init(INITIAL_KERNEL_HEAP_START, INITIAL_KERNEL_HEAP_SIZE as _);

    static MMAP_REQ: MemmapRequest = MemmapRequest::new(0);
    memory::initialize(
        MMAP_REQ
            .get_response()
            .get()
            .expect("tried to get memory map from Limine")
            .memmap(),
    );

    gdt::init();
    interrupts::init();

    static KFILE_REQ: KernelFileRequest = KernelFileRequest::new(0);
    static MOD_REQ: ModuleRequest = ModuleRequest::new(0);
    crate::kmain::kmain(
        KFILE_REQ
            .get_response()
            .get()
            .and_then(|r| r.kernel_file.get())
            .expect("failed to get kernel file from Limine")
            .cmdline
            .to_str()
            .map(core::ffi::CStr::to_str)
            .transpose()
            .expect("expected valid cmdline string")
            .unwrap_or_default(),
        MOD_REQ
            .get_response()
            .get()
            .and_then(|m| m.modules().get(0))
            .map(|file| unsafe {
                core::slice::from_raw_parts(
                    file.base.as_ptr().expect("invalid initrd"),
                    file.length as usize,
                )
            }),
    )
}

/// Spin loop
pub fn sloop() -> ! {
    loop {
        x86_64::instructions::hlt();
    }
}