use limine::{LimineHhdmRequest, LimineMmapRequest};
use spin::Mutex;
use uart_16550::SerialPort;
use x86_64::VirtAddr;

mod allocator;
mod gdt;
mod idt;
mod memory;

static SERIAL_CONSOLE: Mutex<SerialPort> = Mutex::new(unsafe { SerialPort::new(0x3f8) });

#[no_mangle]
unsafe extern "C" fn _kernel_start() -> ! {
    static HDHM_REQ: LimineHhdmRequest = LimineHhdmRequest::new(0);
    static MMAP_REQ: LimineMmapRequest = LimineMmapRequest::new(0);

    SERIAL_CONSOLE.lock().init();
    crate::logger::init().expect("failed to set logger");
    log::info!("Initialising AKern {}", crate::VERSION);

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

    memory::init_falloc(
        MMAP_REQ
            .get_response()
            .get()
            .and_then(limine::LimineMemmapResponse::mmap)
            .expect("tried to get memory map from Limine"),
    );

    allocator::init_alloc().expect("tried to initialise allocator");
    gdt::init();
    idt::init();

    crate::kmain::kmain()
}

/// Format args to serial console
pub fn serial_fmt(args: core::fmt::Arguments<'_>) -> core::fmt::Result {
    use core::fmt::Write;
    x86_64::instructions::interrupts::without_interrupts(|| SERIAL_CONSOLE.lock().write_fmt(args))
}

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