#![no_std]
#![no_main]

pub mod boot_services;
pub mod macros;
pub mod memory_allocation_services;
pub mod simple_filesystem_protocol;
pub mod simple_text_output;
pub mod structs;
pub mod system_table;
pub mod types;

use boot_services::BootServicesTable;
use core::{
    ffi::c_void,
    panic::PanicInfo,
    ptr::{null, null_mut},
};
use simple_filesystem_protocol::{FileProtocol, OpenMode, SimpleFileSystemProtocol};
use simple_text_output::SimpleTextOutputProtocol;
use structs::Status;
use system_table::SystemTable;
use types::Handle;
use registers;
use core::arch::asm;

#[no_mangle]
#[export_name = "efi_main"]
extern "efiapi" fn efi_main(image_handle: Handle, system_table_ptr: *mut SystemTable) -> usize {
    if system_table_ptr.is_null() {
        return usize::from(Status::OutOfResources);
    }

    set_control_registers();

    let system_table: &SystemTable = initialize_system_table(system_table_ptr);
    let console_out: Option<&SimpleTextOutputProtocol> = initialize_console_out(system_table);
    let boot_services_table: Option<&BootServicesTable> = initialize_boot_services(system_table);

    if let Some(console_out) = console_out {
        (console_out.clear_screen)(console_out);
        let mut columns: usize = 0;
        let mut rows: usize = 0;
        let mut mode_number: usize = 0;

        (console_out.output_string)(console_out, utf16_string!("my-os").as_ptr());
        (console_out.output_string)(console_out, utf16_string!("\n").as_ptr());
    }

    loop {}

    usize::from(Status::Success) // EFI_SUCCESS
}

fn initialize_system_table(system_table_ptr: *mut SystemTable) -> &'static SystemTable {
    unsafe { &*system_table_ptr }
}

fn initialize_console_out(system_table: &SystemTable) -> Option<&SimpleTextOutputProtocol> {
    let console_out = system_table.console_out;
    if !console_out.is_null() {
        unsafe { Some(&*console_out) }
    } else {
        None
    }
}

fn initialize_boot_services(system_table: &SystemTable) -> Option<&BootServicesTable> {
    if !system_table.boot_services_table_pointer.is_null() {
        unsafe { Some(&*system_table.boot_services_table_pointer) }
    } else {
        None
    }
}

fn set_control_registers() -> () {
    unsafe {
        registers::CR0::set_protection_enabled_flag(true);
        registers::CR0::set_monitor_coprocessor_flag(true);
        registers::CR0::set_emulation_flag(false);
        registers::CR0::set_task_switched_flag(false);
        registers::CR0::set_extension_type_flag(true);
        registers::CR0::set_numeric_error_flag(true);
        registers::CR0::set_paging_flag(true);
        registers::CR0::set_cache_disable_flag(false);
        registers::CR0::set_non_writethrough_flag(false);
        registers::CR0::set_alignment_mask_flag(false);
        registers::CR0::set_write_protect_flag(false);
        registers::CR4::set_physical_address_extension_flag(true);
        registers::CR4::set_OSFXSR_flag(true);
        registers::CR4::set_OSXMMEXCPT_flag(true);
        registers::CR4::set_OSXSAVE_flag(true);
        registers::CR4::set_SMEP_enable_bit_flag(false);
        registers::CR4::set_SMAP_enable_bit_flag(false);
        registers::CR4::set_PCID_enable_bit_flag(false);
        registers::x87::init_fpu_state();
    }
}

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {}
}
