use crate::config::{PAGE_SIZE, PAGE_SIZE_BITS};
use crate::mm::page_table::PageTableEntry;

const PA_WIDTH_SV39: usize = 56;  // 真实物理地址 的字节宽度  44(物理页宽) + 12(4kb的宽度)
const VA_WIDTH_SV39: usize = 39;  // 虚拟地址的宽度 9 + 9 + 9 + 12
const PPN_WIDTH_SV39: usize = PA_WIDTH_SV39 - PAGE_SIZE_BITS;  // 物理页 的宽度, 也就是 物理地址宽度-每页宽度=物理页宽度
const VPN_WIDTH_SV39: usize = VA_WIDTH_SV39 - PAGE_SIZE_BITS;  // 虚拟页的宽度 27


// 物理地址 56bit 44 12
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct PhysAddr(pub usize);

// 虚拟地址 39bit  9 9 9 12
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct VirtAddr(pub usize);

// 物理页 44bit
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct PhysPageNum(pub usize);

// 虚拟页 29bit 布局为 9 9 9
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct VirtPageNum(pub usize);


// 把usize 转为真实地址(只保留 PA_WIDTH_SV39 位字节)
impl From<usize> for PhysAddr {
    fn from(v: usize) -> Self {
        Self(v & ( (1 << PA_WIDTH_SV39) - 1 ))
    }
}

// 把usize类型的物理页号 转为物理页结构体 (只保留物理页的宽度)
impl From<usize> for PhysPageNum {
    fn from(v: usize) -> Self {
        Self(v & ( (1 << PPN_WIDTH_SV39) - 1 ))
    }
}

// 把物理地址转为 usize, 直接返回即可
impl From<PhysAddr> for usize {
    fn from(v: PhysAddr) -> Self {
        v.0
    }
}

// 把物理页结构体转为 usize类型的物理页 直接返回即可
impl From<PhysPageNum> for usize {
    fn from(v: PhysPageNum) -> Self {
        v.0
    }
}

// 把PhysAddr 转为 PhysPageNum
impl From<PhysAddr> for PhysPageNum {
    fn from(v: PhysAddr) -> Self {
        assert_eq!(v.page_offset(), 0);
        v.floor()
    }
}

// 把物理页转为物理地址(左移页宽即可)
impl From<PhysPageNum> for PhysAddr {
    fn from(v: PhysPageNum) -> Self { Self(v.0 << PAGE_SIZE_BITS) }
}

impl PhysAddr {
    // 从当前物理地址得到页内偏移(只保留12个bit即可)
    pub fn page_offset(&self) -> usize {
        self.0 & (PAGE_SIZE - 1)
    }

    // 舍去小数位, 把物理地址, 转为 物理页号 向下取整
    pub fn floor(&self) -> PhysPageNum {
        PhysPageNum(self.0 / PAGE_SIZE)
    }

    // 有小数统一 +1, 物理地址转为物理页号  向上取整
    pub fn ceil(&self) -> PhysPageNum {
        PhysPageNum((self.0 + PAGE_SIZE - 1) / PAGE_SIZE)
    }

    // 把当前虚拟地址映射为指定类型T
    pub fn get_mut<T>(&self) -> &'static mut T {
        unsafe { (self.0 as *mut T).as_mut().unwrap() }
    }
}


impl PhysPageNum {
    // // 从当前物理页保存的数据中得到所有的 PTE
    pub fn get_pte_array(&self) -> &'static mut [PageTableEntry] {
        // 物理页号 转为物理地址
        let pa: PhysAddr = (*self).into();
        // 从物理页地址读出一页 512个8字节(4k)的数据
        unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut PageTableEntry, 512) }
    }

    // 从当前物理页得到 引用类型的 byte array
    pub fn get_bytes_array(&self) -> &'static mut [u8] {
        let pa: PhysAddr = (*self).into();
        unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut u8, 4096) }
    }

    // 把物理页转为物理地址之后, 再转为T, 强转
    pub fn get_mut<T>(&self) -> &'static mut T {
        let pa: PhysAddr = (*self).into();
        unsafe { (pa.0 as *mut T).as_mut().unwrap() }
    }
}

impl VirtAddr{
    // 舍去小数位, 把虚拟地址转为物理页号 向下取整
    pub fn floor(&self) -> VirtPageNum {
        VirtPageNum(self.0 / PAGE_SIZE)
    }

    // 有小数统一+1 虚拟地址转为虚拟页号 向上取整
    pub fn ceil(&self) -> VirtPageNum {
        VirtPageNum((self.0 - 1 + PAGE_SIZE) / PAGE_SIZE)
    }

    // 虚拟地址得到业内偏移 只保留12个bit即可
    pub fn page_offset(&self) -> usize {
        self.0 & (PAGE_SIZE - 1)
    }
}


// 把usize 转为 虚拟地址, 只保留39位
impl From<usize> for VirtAddr {
    fn from(value: usize) -> Self {
        Self(value & ((1 << VA_WIDTH_SV39) - 1))
    }
}

// 把虚拟地址, 转为usize
impl From<VirtAddr> for usize {
    fn from(v: VirtAddr) -> Self {
        if v.0 >= (1 << (VA_WIDTH_SV39 - 1)) {
            // 将高位全部设置为1, 虚拟地址就是这样的, 64~39 位, 随着38位变化
            v.0 | (!((1 << VA_WIDTH_SV39) - 1))
        } else {
            v.0
        }
    }
}

// 把usize类型的页号 转为 虚拟页号结构体, 只保留27位即可
impl From<usize> for VirtPageNum {
    fn from(v: usize) -> Self {
        Self(v & ((1 << VPN_WIDTH_SV39) - 1))
    }
}

// 虚拟地址 转为虚拟页号 向下取整
impl From<VirtAddr> for VirtPageNum {
    fn from(v: VirtAddr) -> Self {
        assert_eq!(v.page_offset(), 0);
        v.floor()
    }
}

// 虚拟页号 转为虚拟地址 左移即可
impl From<VirtPageNum> for VirtAddr {
    fn from(v: VirtPageNum) -> Self {
        // 左移12位
        Self(v.0 << PAGE_SIZE_BITS)
    }
}


impl VirtPageNum{
    // 从虚拟页号中 抽离 9 9 9 的布局
    pub fn indexes(&self) -> [usize; 3] {
        let mut vpn = self.0;
        let mut idx = [0usize; 3];
        for i in (0..3).rev() {
            idx[i] = vpn & 511;
            vpn >>= 9;
        }
        idx
    }
}


// 代表一个range类型, 表示一段 虚拟地址 的区间
#[derive(Copy, Clone)]
pub struct VPNRange {
    pub l: VirtPageNum,
    pub r: VirtPageNum
}

impl Iterator for VPNRange {
    type Item = VirtPageNum;

    fn next(&mut self) -> Option<Self::Item> {
        if self.l == self.r {
            None
        } else {
            let current_vpn = self.l;
            self.l = (self.l.0 + 1).into();
            Some(current_vpn)
        }
    }
}