use yggdrasil_abi::error::Error;

use crate::{
    address::PhysicalAddress,
    table::{MapAttributes, TableAllocator},
};

pub struct PageAttributeUpdate {
    pub user_write: Option<bool>,
    pub dirty: Option<bool>,
}

/// Interface for virtual memory address space management
pub trait ProcessAddressSpaceManager<TA: TableAllocator>: Sized {
    /// PFN of a minimum address allowed for virtual region allocation
    const LOWER_LIMIT_PFN: usize;
    /// PFN of a maximum address allowed for virtual region allocation
    const UPPER_LIMIT_PFN: usize;

    /// Constructs a new implementation-specific per-process address space
    fn new() -> Result<Self, Error>;

    /// Places a single PAGE_SIZE mapping into the address space.
    ///
    /// # Safety
    ///
    /// The caller must ensure the correct origin of the physical address being mapped.
    unsafe fn map_page(
        &mut self,
        address: usize,
        physical: PhysicalAddress,
        flags: MapAttributes,
    ) -> Result<(), Error>;

    /// Adds/removes attributes from a page entry in the address space.
    ///
    /// # Safety
    ///
    /// The caller must ensure the validity of this update.
    unsafe fn update_page_attributes(
        &mut self,
        address: usize,
        update: &PageAttributeUpdate,
    ) -> Result<(), Error>;

    /// Removes a single PAGE_SIZE mapping from the address space.
    ///
    /// # Safety
    ///
    /// The caller must ensure the process to which this address space belongs does not and
    /// will not access this page.
    unsafe fn unmap_page(&mut self, address: usize) -> Result<(PhysicalAddress, bool), Error>;

    /// Returns the [PhysicalAddress] and [MapAttributes] associated with given virtual `address`,
    /// if one is mapped
    fn translate(&self, address: usize) -> Result<(PhysicalAddress, MapAttributes), Error>;

    /// Returns the physical address of the translation table along with its ASID
    fn as_address_with_asid(&self) -> (u64, u64);

    /// Clears the address space by dropping and non-global tables.
    ///
    /// # Safety
    ///
    /// Meant to be called when a process is being dropped or a new image is being loaded,
    /// replacing the old mappings. All the mappings become invalid after this call.
    unsafe fn clear(&mut self);
}
