use core::ptr::NonNull;

pub const PAGE_SIZE: usize = 0x1000;

pub trait PageProvider: Sync {
    fn map_pages(count: usize) -> Option<NonNull<u8>>;
    fn unmap_pages(address: NonNull<u8>, count: usize);
}

#[cfg(any(not(feature = "dep-of-kernel"), rust_analyzer))]
pub struct OsPageProvider;

#[cfg(any(all(unix, not(feature = "dep-of-kernel")), rust_analyzer))]
impl PageProvider for OsPageProvider {
    fn map_pages(count: usize) -> Option<NonNull<u8>> {
        use core::ptr::null_mut;

        let address = unsafe {
            libc::mmap(
                null_mut(),
                count * PAGE_SIZE,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_ANONYMOUS | libc::MAP_PRIVATE,
                -1,
                0,
            )
        };

        NonNull::new(address as *mut u8)
    }

    fn unmap_pages(address: NonNull<u8>, count: usize) {
        unsafe {
            libc::munmap(address.as_ptr() as _, count * PAGE_SIZE);
        }
    }
}

#[cfg(any(all(not(unix), not(feature = "dep-of-kernel")), rust_analyzer))]
impl PageProvider for OsPageProvider {
    fn map_pages(count: usize) -> Option<NonNull<u8>> {
        use yggdrasil_rt::mem::{MappingFlags, MappingSource};

        let address = unsafe {
            yggdrasil_rt::sys::map_memory(
                None,
                count * PAGE_SIZE,
                MappingFlags::WRITE,
                &MappingSource::Anonymous,
            )
        }
        .ok()?;

        NonNull::new(core::ptr::with_exposed_provenance_mut(address))
    }

    fn unmap_pages(address: NonNull<u8>, count: usize) {
        unsafe {
            yggdrasil_rt::sys::unmap_memory(address.addr().into(), count * PAGE_SIZE).unwrap();
        }
    }
}
