use {
    crate::holeybytes::{kernel_services::block_read, Vm},
    alloc::alloc::{alloc, dealloc},
    core::alloc::Layout,
    log::{debug, info},
};

pub enum MemoryServiceError {
    InvalidMemoryFormat,
}
#[derive(Debug)]
pub enum MemoryQuotaType {
    NoQuota = 0,
    SoftQuota = 1,
    HardQuota = 2,
    KillQuota = 3,
}

fn alloc_page(vm: &mut Vm, _mem_addr: u64, _length: usize) -> Result<(), MemoryServiceError> {
    let ptr = unsafe { alloc(Layout::from_size_align_unchecked(4096, 8)) };
    info!("Block address: {:?}", ptr);
    vm.registers[1] = hbvm::value::Value(ptr as u64);
    vm.registers[2] = hbvm::value::Value(4096);
    Ok(())
}

#[inline(always)]
unsafe fn memset(dest: *mut u8, src: *const u8, count: usize, size: usize) {
    let total_size = count * size;
    src.copy_to_nonoverlapping(dest, size);

    let mut copied = size;

    while copied < total_size {
        let copy_size = copied.min(total_size - copied);
        dest.add(copied).copy_from_nonoverlapping(dest, copy_size);
        copied += copy_size;
    }
}

#[inline(always)]
pub fn memory_msg_handler(
    vm: &mut Vm,
    mem_addr: u64,
    length: usize,
) -> Result<(), MemoryServiceError> {
    let msg_vec = block_read(mem_addr, length);
    let msg_type = msg_vec[0];
    match msg_type {
        0 => unsafe {
            let page_count = msg_vec[1];

            let ptr = alloc(Layout::from_size_align_unchecked(
                page_count as usize * 4096,
                8,
            ));

            log::debug!("Allocating {} pages @ {:x}", page_count, ptr as u64);

            vm.registers[1] = hbvm::value::Value(ptr as u64);
            log::debug!("Kernel ptr: {:x}", ptr as u64);
        },

        1 => unsafe {
            let page_count = msg_vec[1];

            let mptr_raw: [u8; 8] = msg_vec[2..10].try_into().unwrap();
            let mptr: u64 = u64::from_le_bytes(mptr_raw);
            log::debug!("Deallocating {} pages @ {:x}", page_count, mptr);

            dealloc(
                mptr as *mut u8,
                Layout::from_size_align_unchecked(page_count as usize * 4096, 8),
            )
        },
        2 => {
            use MemoryQuotaType::*;
            let quota_type = match msg_vec[1] {
                0 => NoQuota,
                1 => SoftQuota,
                2 => HardQuota,
                3 => KillQuota,
                _ => NoQuota,
            };
            let hid_raw: [u8; 8] = msg_vec[2..10].try_into().unwrap();
            let hid: u64 = u64::from_le_bytes(hid_raw);
            let pid_raw: [u8; 8] = msg_vec[10..18].try_into().unwrap();
            let pid: u64 = u64::from_le_bytes(pid_raw);
            debug!(
                "Setting HID-{:x}:PID-{:x}'s quota type to {:?}",
                hid, pid, quota_type
            )
        }
        3 => {
            let page_count = msg_vec[1];
            log::debug!(" {} pages", page_count);
        }
        4 => unsafe {
            let count = u64::from_le_bytes(msg_vec[1..9].try_into().unwrap_unchecked()) as usize;
            let src = u64::from_le_bytes(msg_vec[9..17].try_into().unwrap_unchecked()) as *const u8;
            let dest = u64::from_le_bytes(msg_vec[17..25].try_into().unwrap_unchecked()) as *mut u8;
            debug_assert!(src.addr() & 0xFFFF000000000000 != 0);
            debug_assert!(dest.addr() & 0xFFFF000000000000 != 0);
            src.copy_to_nonoverlapping(dest, count);
        },
        5 => unsafe {
            let count = u64::from_le_bytes(msg_vec[1..9].try_into().unwrap_unchecked()) as usize;
            let size = u64::from_le_bytes(msg_vec[9..17].try_into().unwrap_unchecked()) as usize;
            let src =
                u64::from_le_bytes(msg_vec[17..25].try_into().unwrap_unchecked()) as *const u8;
            let dest = u64::from_le_bytes(msg_vec[25..33].try_into().unwrap_unchecked()) as *mut u8;
            debug_assert!(src.addr() & 0xFFFF000000000000 != 0);
            debug_assert!(dest.addr() & 0xFFFF000000000000 != 0);
            memset(dest, src, count, size);
        },
        _ => {
            log::debug!("Unknown memory service message type: {}", msg_type);
        }
    }

    Ok(())
}