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(mut dest: *mut u8, src: *const u8, count: usize, size: usize) {
    const BLOCK_SIZE: usize = 64;
    let mut remaining = count * size;

    if remaining < 16 {
        src.copy_to_nonoverlapping(dest, remaining);
        return;
    }

    let mut buffer = [0u8; BLOCK_SIZE];
    let mut buffer_size = size;
    src.copy_to_nonoverlapping(buffer.as_mut_ptr(), size);

    while core::intrinsics::likely(buffer_size * 2 <= BLOCK_SIZE) {
        buffer
            .as_mut_ptr()
            .copy_to_nonoverlapping(buffer.as_mut_ptr().add(buffer_size), buffer_size);
        buffer_size *= 2;
    }

    let buffer_ptr = buffer.as_ptr() as *const u64;

    while (dest as usize) & 7 != 0 && remaining >= 8 {
        buffer.as_ptr().copy_to_nonoverlapping(dest, 1);
        dest = dest.add(1);
        remaining -= 1;
    }

    while core::intrinsics::likely(remaining >= 8) {
        *(dest as *mut u64) = *buffer_ptr;
        dest = dest.add(8);
        remaining -= 8;
    }

    if remaining > 0 {
        buffer.as_ptr().copy_to_nonoverlapping(dest, remaining);
    }
}

#[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 = u32::from_le_bytes(msg_vec[1..5].try_into().unwrap_unchecked()) as usize;
            let src = u64::from_le_bytes(msg_vec[5..13].try_into().unwrap_unchecked()) as *const u8;
            let dest = u64::from_le_bytes(msg_vec[13..21].try_into().unwrap_unchecked()) as *mut u8;

            src.copy_to_nonoverlapping(dest, count);
        },
        5 => unsafe {
            let count = u32::from_le_bytes(msg_vec[1..5].try_into().unwrap_unchecked()) as usize;
            let size = u32::from_le_bytes(msg_vec[5..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;

            memset(dest, src, count, size);
        },
        _ => {
            log::debug!("Unknown memory service message type: {}", msg_type);
        }
    }

    Ok(())
}