use alloc::vec::Vec;
use lazy_static::lazy_static;
use crate::mm::address::{PhysPageNum, PhysAddr};
use crate::config::MEMORY_END;
use crate::println;
use crate::sync::UPSafeCell;

extern "C" {
    fn ekernel();
}

// 物理页帧的结构体, 他和物理页号不同, 它是管理整个物理页内的数据, 而物理页号被页表管理
// 这个结构体只是利用rust drop管理变量的方式
// 我们使用这个结构体, 来利用rust变量生命周期, 自动管理该页, 数据清零或者还回物理页帧管理器
// 这个结构可以省去(因为他很多时候使用PhysPageNum 也可以完成), 但是为了防止后续课程还有其他地方使用, 就不省了,
pub struct FrameTracker {
    pub ppn: PhysPageNum,
}

impl FrameTracker {
    pub fn new(ppn: PhysPageNum) -> Self {
        // 页数据初始化
        let bytes_array = ppn.get_bytes_array();
        for i in bytes_array {
            *i = 0;
        }
        Self { ppn }
    }
}

impl Drop for FrameTracker {
    // 生命周期结束, 自动把页还给 物理页帧管理器
    fn drop(&mut self) {
        frame_dealloc(self.ppn);
    }
}


// 物理页帧管理器, 它管理了 ekernel(操作系统使用的内存结束位置) 到 MEMORY_END(最大的可用的物理内存地址)所有的内存, 这部分内存是给用户用的
// current, end 这个区间表示此前从未被分配过的页
// recycled 表示之前被分配过, 但是已经被回收可以再利用的页
pub struct StackFrameAllocator {
    current: PhysPageNum,
    end: PhysPageNum,
    recycled: Vec<PhysPageNum>,
}

impl StackFrameAllocator {
    fn from(l: PhysPageNum, r: PhysPageNum) -> Self {
        Self {
            current: l,
            end: r,
            recycled: Vec::new(),
        }
    }
}

impl StackFrameAllocator {
    // 从分配器中, 找到一个空闲页(它可能之前从未分配过, 或者之前被分配但是已经被回收的完好页)
    fn alloc(&mut self) -> Option<PhysPageNum> {
        if let Some(ppn) = self.recycled.pop() {
            Some(ppn)
        } else if self.current == self.end {
            None
        } else {
            let current_ppn = self.current;
            let next_ppn = (self.current.0 + 1).into();
            self.current = next_ppn;
            Some(current_ppn)
        }
    }

    // 回收一个页, 为了防止二次回收, 我们这里直接panic
    fn dealloc(&mut self, ppn: PhysPageNum) {
        // 防止被二次回收
        if ppn >= self.current || self.recycled.iter().any(|&v| v == ppn) {
            panic!("Frame ppn={:#x} has not been allocated!", ppn.0);
        }
        // 校验完毕, 把这个页, 还给管理器
        self.recycled.push(ppn);
    }
}

lazy_static! {
    static ref FRAME_ALLOCATOR: UPSafeCell<StackFrameAllocator> = unsafe {
        // 这里我们只要完整的页, 舍去开头和结尾一些碎片
        UPSafeCell::new(StackFrameAllocator::from(PhysAddr::from(ekernel as usize).ceil(), PhysAddr::from(MEMORY_END).floor()))
    };
}

/// 分配一个页
pub fn frame_alloc() -> Option<FrameTracker> {
    FRAME_ALLOCATOR
        .exclusive_access()
        .alloc()
        .map(FrameTracker::new)
}

/// 回收指定的页
fn frame_dealloc(ppn: PhysPageNum) {
    FRAME_ALLOCATOR.exclusive_access().dealloc(ppn);
}


pub fn frame_allocator_test() {
    let mut v: Vec<FrameTracker> = Vec::new();
    for i in 0..5 {
        let frame = frame_alloc().unwrap();
        println!("frame_allocator_test {:?}", frame.ppn.0);
        v.push(frame);
    }
    v.clear();
    for i in 0..5 {
        let frame = frame_alloc().unwrap();
        println!("frame_allocator_test {:?}", frame.ppn.0);
        v.push(frame);
    }
    drop(v);
    println!("frame_allocator_test passed!");
}
