use crate::MemoryError;

/// Reserve a contiguous virtual address region without committing physical pages.
#[cfg(unix)]
pub fn reserve_address_space(size: usize) -> Result<*mut u8, MemoryError> {
    unsafe {
        let ptr = libc::mmap(
            std::ptr::null_mut(),
            size,
            libc::PROT_NONE,
            libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_NORESERVE,
            -1,
            0,
        );
        if ptr == libc::MAP_FAILED {
            Err(MemoryError::AllocationFailed(format!(
                "mmap failed for {} bytes: {}",
                size,
                std::io::Error::last_os_error()
            )))
        } else {
            Ok(ptr as *mut u8)
        }
    }
}

/// Commit (make accessible) a region within a previously reserved address space.
#[cfg(unix)]
pub fn commit_memory(ptr: *mut u8, size: usize) -> Result<(), MemoryError> {
    unsafe {
        let result = libc::mprotect(ptr as *mut libc::c_void, size, libc::PROT_READ | libc::PROT_WRITE);
        if result != 0 {
            Err(MemoryError::AllocationFailed(format!(
                "mprotect failed for {} bytes: {}",
                size,
                std::io::Error::last_os_error()
            )))
        } else {
            Ok(())
        }
    }
}

/// Release a previously reserved address space.
#[cfg(unix)]
pub unsafe fn release_address_space(ptr: *mut u8, size: usize) {
    unsafe { libc::munmap(ptr as *mut libc::c_void, size); }
}

#[cfg(windows)]
pub fn reserve_address_space(size: usize) -> Result<*mut u8, MemoryError> {
    unsafe {
        let ptr = windows_sys::Win32::System::Memory::VirtualAlloc(
            std::ptr::null_mut(),
            size,
            windows_sys::Win32::System::Memory::MEM_RESERVE,
            windows_sys::Win32::System::Memory::PAGE_NOACCESS,
        );
        if ptr.is_null() {
            Err(MemoryError::AllocationFailed(format!(
                "VirtualAlloc reserve failed for {} bytes",
                size,
            )))
        } else {
            Ok(ptr as *mut u8)
        }
    }
}

#[cfg(windows)]
pub fn commit_memory(ptr: *mut u8, size: usize) -> Result<(), MemoryError> {
    unsafe {
        let result = windows_sys::Win32::System::Memory::VirtualAlloc(
            ptr as *mut _,
            size,
            windows_sys::Win32::System::Memory::MEM_COMMIT,
            windows_sys::Win32::System::Memory::PAGE_READWRITE,
        );
        if result.is_null() {
            Err(MemoryError::AllocationFailed(format!(
                "VirtualAlloc commit failed for {} bytes",
                size,
            )))
        } else {
            Ok(())
        }
    }
}

#[cfg(windows)]
pub unsafe fn release_address_space(ptr: *mut u8, size: usize) {
    let _ = size;
    windows_sys::Win32::System::Memory::VirtualFree(
        ptr as *mut _,
        0,
        windows_sys::Win32::System::Memory::MEM_RELEASE,
    );
}
