use core::mem::size_of;

use bytemuck::{Pod, Zeroable};

use crate::{AvailableRegion, IterableMemoryMap, LoadProtocolHeader};

pub const PIXEL_R8G8B8A8: u32 = 1;
pub const PIXEL_B8G8R8A8: u32 = 2;

#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct LoadProtocol {
    pub header: LoadProtocolHeader,
    pub kernel_virt_offset: u64,

    pub memory_map: MemoryMap,

    pub cmdline: u64,
    pub cmdline_len: u64,

    pub rsdp_address: u64,
    pub initrd_address: u64,
    pub initrd_size: u64,

    // Load options
    pub opt_framebuffer: FramebufferOption,
}

#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct AvailableMemoryRegion {
    pub start_address: u64,
    pub page_count: u64,
}

#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct MemoryMap {
    pub address: u64,
    pub len: u64,
}

// TODO specify format etc.
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct FramebufferOption {
    pub req_width: u32,
    pub req_height: u32,

    pub res_width: u32,
    pub res_height: u32,
    pub res_stride: u64,
    pub res_address: u64,
    pub res_size: u64,
    pub res_format: u32,
    pub _0: u32,
}

impl AvailableRegion for AvailableMemoryRegion {
    fn start_address(&self) -> usize {
        self.start_address as _
    }

    fn page_count(&self) -> usize {
        self.page_count as _
    }
}

impl IterableMemoryMap<'static> for MemoryMap {
    type Entry = AvailableMemoryRegion;
    type Iter = core::slice::Iter<'static, AvailableMemoryRegion>;

    fn iter_with_offset(&self, offset: usize) -> Self::Iter {
        let virt_address = self.address + offset as u64;
        let slice = unsafe {
            core::slice::from_raw_parts(virt_address as *const AvailableMemoryRegion, self.len as _)
        };

        slice.iter()
    }

    fn data_physical_base(&self) -> usize {
        self.address as _
    }

    fn data_size(&self) -> usize {
        self.len as usize * size_of::<AvailableMemoryRegion>()
    }
}
