#![no_std]
#![feature(abi_x86_interrupt)]

extern crate alloc;
extern crate multiboot2;
extern crate rlibc;

mod allocator;
mod gdt;
mod interrupt;
mod memory;
mod vga_buffer;

use core::panic::PanicInfo;
use multiboot2::{BootInformation, BootInformationHeader};

#[unsafe(no_mangle)]
pub extern "C" fn rust_main(multiboot_information_address: usize) {
    println!("Hello World!");

    // get multiboot info
    let boot_info = unsafe {
        BootInformation::load(multiboot_information_address as *const BootInformationHeader)
            .unwrap()
    };
    let memory_map_tag = boot_info.memory_map_tag().expect("Memory map tag required");
    println!("memory areas:");
    for area in memory_map_tag.memory_areas() {
        println!(
            "  start: 0x{:x}, length: 0x{:x}",
            area.start_address(),
            area.size()
        );
    }

    let elf_sections_tag = boot_info
        .elf_sections_tag()
        .expect("Elf sections tag required");
    println!("kernel sections:");
    for section in elf_sections_tag.sections() {
        println!(
            "  addr: 0x{:x}, size: 0x{:x}, flags: 0x{:x}",
            section.start_address(),
            section.size(),
            section.flags()
        );
    }

    let kernel_start = elf_sections_tag
        .sections()
        .map(|s| s.start_address())
        .min()
        .unwrap();
    let kernel_end = elf_sections_tag
        .sections()
        .map(|s| s.end_address())
        .max()
        .unwrap();
    let multiboot_start = multiboot_information_address;
    let multiboot_end = multiboot_start + boot_info.total_size() as usize;

    println!(
        "kernel_start: 0x{:x} kernel_end: 0x{:x} kernel_size: {}B\nmultiboot_start: 0x{:x} multiboot_end: 0x{:x} multiboot_size: {}B",
        kernel_start,
        kernel_end,
        kernel_end - kernel_start,
        multiboot_start,
        multiboot_end,
        multiboot_end - multiboot_start
    );
    //init();
    halt()
}

pub fn init() {
    gdt::init();
    interrupt::init_idt();
    unsafe { interrupt::PICS.lock().initialize() };
    x86_64::instructions::interrupts::enable();
}

#[panic_handler]
pub fn panic(info: &PanicInfo) -> ! {
    println!("Panic happened: {}", info);
    halt()
}

pub fn halt() -> ! {
    println!("we've halted");
    loop {
        x86_64::instructions::hlt();
    }
}
