use alloc::sync::Arc;
use lazy_static::lazy_static;
use crate::println;
use crate::task::context::TaskContext;
use crate::task::switch::__switch;
use crate::task::task::{TaskControlBlock, TaskStatus};
use crate::trap::TrapContext;
use crate::sync::UPSafeCell;
use crate::task::manager::{pop_task, TASK_MANAGER};
use crate::timer::{get_time_ms, get_time_us};

///维护在一个处理器上正在执行的任务, 这在ch4 中是 TaskManager做的, 现在拆分
pub struct Processor {
    /// 当前处理器上正在执行的任务, 他是一个被option 包裹的 智能指针, 保证 TCB 要不在这个结构体中, 要不就是在全局的任务管理器上
    current: Option<Arc<TaskControlBlock>>,
    /// 当前处理器上的 idle 控制流的任务上下文, 当任务切换的时候, 先切换到这个
    /// 这样做的主要目的是使得换入/换出进程和调度执行流在内核层各自执行在不同的内核栈上,
    /// 分别是进程自身的内核栈和内核初始化时使用的启动栈
    /// 这样的话, 调度相关的数据不会出现在进程内核栈上,
    /// 也使得调度机制对于换出进程的Trap执行流是不可见的
    /// 它在决定换出的时候只需调用schedule而无需操心调度的事情
    /// 从而各执行流的分工更加明确了, 虽然带来了更大的开销
    /// 虽然 永远不会执行这个 task context 的任务, 他只保存 内核的启动栈
    idle_task_cx: TaskContext,
    pub clock_time: usize,  // 处理器时间
}


impl Processor {
    ///Create an empty Processor
    /// 一旦程序开始运行, 就开始在内核栈或者用户栈之间切换, 永远 不会运行到这个, 这个就是保存 内核的启动栈的
    pub fn new() -> Self {
        Self {
            current: None,
            idle_task_cx: TaskContext::new(),
            clock_time: 0
        }
    }
}

impl Processor {
    // 得到当前 task context 的指针
    fn get_idle_task_cx_ptr(&mut self) -> *mut TaskContext {
        &mut self.idle_task_cx as *mut _
    }
    /// 取出当前正在执行的任务
    pub fn take_current(&mut self) -> Option<Arc<TaskControlBlock>> {
        self.current.take()
    }

    ///当前正在执行的任务的 copy
    pub fn current(&self) -> Option<Arc<TaskControlBlock>> {
        self.current.as_ref().map(Arc::clone)
    }

    // 掐表函数
    fn refresh_stop_clock(&mut self) -> usize {
        // 上次停表的时间
        let start_time = self.clock_time;
        // 当前时间
        self.clock_time = get_time_us();
        // 返回 当前时间 - 上次停表时间 = 时间间距
        self.clock_time - start_time
    }

    // 即将进入用户态, 把之前使用的内核时间统计
    fn user_time_start(&mut self) {
        let use_clock_time = self.refresh_stop_clock();
        self.current.as_ref().unwrap().inner_exclusive_access().kernel_time += use_clock_time;
    }

    fn kernel_time_start(&mut self) {
        let use_clock_time = self.refresh_stop_clock();
        self.current.as_ref().unwrap().inner_exclusive_access().user_time += use_clock_time;
    }
}



// 全局 描述cpu状态的管理器
lazy_static! {
    pub static ref PROCESSOR: UPSafeCell<Processor> = unsafe { UPSafeCell::new(Processor::new()) };
}


// 循环 全局的任务管理器'fetch_task'获取需要运行的下一个进程, 并通过'__switch' 和当前process切换进程,
// 这和之前的 run_first_task 差不多
pub fn run_tasks() {
    loop {
        // 获得当前 进程的管理对象
        let mut processor = PROCESSOR.exclusive_access();

        // 弹出一个任务
        if let Some(task) = pop_task() {
            // 当前运行的进程(第一次运行时,这里是空的 zero, 但是不影响, 因为永远不会执行到第一次运行的任务)
            let idle_task_cx_ptr = processor.get_idle_task_cx_ptr();

            // 获得下一个需要运行的进程 的task context的指针
            let mut task_inner = task.inner_exclusive_access();
            let next_task_cx_ptr = &task_inner.task_cx as *const TaskContext;
            task_inner.task_status = TaskStatus::Running;

            // 删除内部可变性的引用, 这里需要手动释放
            drop(task_inner);

            // 修改process的状态, 把下一个需要运行的进程 move到 Process中被管理, 保证引用计数为1
            processor.current = Some(task);

            // 这里需要手动释放, 同上, 因为switch跳出作用域了, 不会通过作用域自动析构 refcell
            drop(processor);

            unsafe {
                __switch(idle_task_cx_ptr, next_task_cx_ptr);

                // 调用 schedule 之后 内部又调用__switch, next task 也就是idle task会走入到这里, 开启新的循环获取新的任务
                // 其实 idle task 并不会执行, 他只是保存 内核的启动栈 也就是当前函数 的寄存器状态
            }
        }
    }
}


// 掐表函数
pub fn refresh_stop_clock() -> usize {
    PROCESSOR.exclusive_access().refresh_stop_clock()
}

// 用户应用 进入用户态开始计时
pub fn user_time_start(){
    PROCESSOR.exclusive_access().user_time_start();
}

// 用户应用 进入内核态开始计时
pub fn kernel_time_start(){
    PROCESSOR.exclusive_access().kernel_time_start();
}

// 取出当前正在执行的任务
pub fn take_current_task() -> Option<Arc<TaskControlBlock>> {
    PROCESSOR.exclusive_access().take_current()
}

// 返回当前正在执行的任务的 copy版本
pub fn current_task() -> Option<Arc<TaskControlBlock>> {
    PROCESSOR.exclusive_access().current()
}

//得到当前正在执行任务的token
pub fn current_user_token() -> usize {
    // 得到当前正在执行的任务
    let task = current_task().unwrap();
    // 得到当前正在执行任务的token
    let token = task.inner_exclusive_access().get_user_token();
    token
}

// 得到当前 task context 的指针
pub fn current_trap_cx() -> &'static mut TrapContext {
    current_task()
        .unwrap()
        .inner_exclusive_access()
        .get_trap_cx()
}

// 扩张/缩减, 当前运行进程的地址空间中的堆段, 指定字节的数据
pub fn change_program_brk(size: i32) -> Option<usize> {
    current_task().as_ref().unwrap().inner_exclusive_access().change_program_brk(size)
}


// 传入当前运行的任务指针, 切换到 idle 任务
// 用尽时间片, 或者调用 yield之后会调用这个, 调用这个之后, 会回到 run_task __switch返回的位置, 开启下一轮循环 中执行
pub fn schedule(switched_task_cx_ptr: *mut TaskContext) {
    // 进程管理对象
    let mut processor = PROCESSOR.exclusive_access();

    // 得到当前进程管理中 task context 指针
    let idle_task_cx_ptr = processor.get_idle_task_cx_ptr();
    drop(processor);

    // 切换 汇编会保存当前的寄存器状态到 switched_task_cx_ptr 中, 然后根据 idle_task_cx_ptr 继续执行 __switch 的下一行, 结束当前循环, 开启新的循环
    unsafe {
        __switch(switched_task_cx_ptr, idle_task_cx_ptr);
    }
}


