// Copyright (c) 2025 shibedrill
// SPDX-License-Identifier: MIT

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

mod arch;
mod boot;
mod constants;
mod interrupt;
#[macro_use]
mod log;
mod memory;
mod panic;
mod process;
mod util;

use arch::x86_64::interrupts::IDT;
use arch::x86_64::serial::SerialPort;
use boot::{BASE_REVISION, params, *};
use constants::*;
use log::*;
use memory::alloc::{boxed::Box, format, string::String, vec};
use params::*;

use lazy_static::lazy_static;
use limine::firmware_type::FirmwareType;
use spin::mutex::Mutex;

use crate::{arch::x86_64::cpuid::{CPUID, virt_supported}, memory::HHDM_RESPONSE};

lazy_static! {
    pub static ref SERIAL_3F8: Mutex<SerialPort> = Mutex::new(
        arch::x86_64::serial::SerialPort::try_from_port(0x3f8).expect("Could not create port")
    );
}

#[unsafe(no_mangle)]
unsafe extern "C" fn main() -> ! {
    // CRITICAL AREA
    // Nothing non-essential should be done until we can initialize logging
    // Otherwise it will fail silently

    // Assert supported bootloader version
    assert!(BASE_REVISION.is_supported());

    // Set up logging level from params
    // Nothing we can do here if this fails since no log subscribers are initialized yet
    if let Some(level) = PARAMS.get("-loglevel")
        && let Ok(parsed_level) = LogLevel::try_from(level.as_str())
    {
        LOGGER.lock().level = parsed_level;
    }
    // Add subscribers to logger
    if let Some(device) = PARAMS.get("-logdev") {
        let log_device_list: vec::Vec<&str> = device.split(',').collect();
        if log_device_list.contains(&"display") {
            // Append display console to log subs
        }
        if log_device_list.contains(&"serial") {
            // TODO: Set up device discovery
            let serial_logger = |msg: &str| SERIAL_3F8.lock().log_writeln(msg);
            LOGGER.lock().add_device(Box::new(serial_logger));
        }
        log_trace!("Configured kernel logging devices");
    }

    // END OF CRITICAL AREA
    // Fallible code can be placed below this comment

    // Initialize all statics so the bootloader memory can be reclaimed
    memory::init_statics();

    // Ensure IDT exists
    IDT.load();

    log_info!(
        "Kernel cmdline: {}",
        String::from_utf8_lossy(
            limine::file::File::string(EXECUTABLE_FILE_RESPONSE.file()).to_bytes()
        )
    );
    log_info!(
        "Kernel file path: {}",
        String::from_utf8_lossy(EXECUTABLE_FILE_RESPONSE.file().path().to_bytes())
    );

    // Branding
    for line in ASCII_LOGO {
        let mut locked = SERIAL_3F8.lock();
        locked.log_write("\t\t");
        locked.log_writeln(line);
    }
    SERIAL_3F8
        .lock()
        .log_writeln("\tWelcome to the Gila microkernel!\n");
    log_info!("Booting gila version {}", kernel_version_string());

    match boot::FIRMWARE_TYPE_REQUEST.get_response() {
        Some(resp) => log_info!(
            "Firmware type: {}",
            match resp.firmware_type() {
                FirmwareType::SBI => "SBI",
                FirmwareType::UEFI_32 => "UEFI (32-bit)",
                FirmwareType::UEFI_64 => "UEFI (64-bit)",
                FirmwareType::X86_BIOS => "x86 BIOS",
                _ => "Unknown",
            }
        ),
        None => log_warning!("Firmware type: No response"),
    }

    log_info!("Trans rights!");

    if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.get_response() {
        let fb = framebuffer_response.framebuffers().next().unwrap();
        log_info!("Framebuffer response received");
        log_trace!(
            "Framebuffer dimensions: {}x{}, {} bits per pixel",
            fb.width(),
            fb.height(),
            fb.bpp()
        );
    } else {
        log_info!("Framebuffer response absent, graphics disabled",);
    }

    let smp_response = MP_REQUEST.get_response();
    match smp_response {
        None => log_info!("Multiprocessing response not received"),
        Some(resp) => {
            log_info!("Multiprocessing response received");
            log_trace!("{} CPUs found", resp.cpus().len());
        }
    }

    boot::modules::log_modules();

    memory::log_memory();
    memory::log_address();

    if let Some(string) = CPUID.get_processor_brand_string() {
        log_info!("CPU brand string: {}", string.as_str());
    }
    if let Some(string) = CPUID.get_vendor_info() {
        log_info!("CPU vendor: {}", string.as_str());
    }
    if let Some(string) = CPUID.get_hypervisor_info() {
        log_info!("Hypervisor: {:?}", string.identify());
    }
    log_info!("Virtualization provider: {:?}", virt_supported());

    let l4_start = x86_64::registers::control::Cr3::read().0.start_address();
    log_info!("Physical address of current L4 table: 0x{:x}", l4_start);
    log_info!("Virtual address of current L4 table: 0x{:x}", l4_start.as_u64() + HHDM_RESPONSE.offset());

    //iter_table(4, &PML4);

    panic!("Finished boot, but cannot start init because processes not implemented!");
}
