use lazy_static::lazy_static;
use alloc::vec::Vec;
use crate::config::MAX_APP_NUM;
use crate::sync::UPSafeCell;
use crate::task::task::{TaskControlBlock, TaskStatus};
use crate::loader::{get_num_app, get_app_data};
use crate::println;
use crate::sbi::shutdown;
use crate::task::context::TaskContext;
use crate::task::manager::{add_task, TASK_MANAGER};
use crate::task::processor::{kernel_time_start, refresh_stop_clock, schedule, take_current_task};
use crate::task::switch::__switch;
use crate::timer::{get_time_ms, get_time_us};
use crate::trap::TrapContext;
use alloc::sync::Arc;
use crate::loader::get_app_data_by_name;

mod context;
mod switch;
pub mod task;
mod pid;
pub mod manager;
pub mod processor;

pub use processor::{run_tasks};

pub const IDLE_PID: usize = 0;

lazy_static! {
    /// 初始化用户shell的全局进程, 他的pid是0
    pub static ref INITPROC: Arc<TaskControlBlock> = Arc::new(TaskControlBlock::from(get_app_data_by_name("initproc").unwrap()));
}

/// 添加初始化 Process 到 全局任务管理器
pub fn add_initproc() {
    add_task(INITPROC.clone());
}

// 挂起当前运行下一个, 需要把当前任务 从Process中 take 取出来, 然后再 push回 全局的任务管理器
pub fn suspend_current_and_run_next() {

    // 必须有一个程序在运行,
    let task = take_current_task().unwrap();

    // ---- access current TCB exclusively
    // 当前正在执行的Process
    let mut task_inner = task.inner_exclusive_access();
    let task_cx_ptr = &mut task_inner.task_cx as *mut TaskContext;
    // Change status to Ready
    task_inner.task_status = TaskStatus::Ready;

    // 统计内核时间
    let kernel_time = refresh_stop_clock();
    task_inner.kernel_time += kernel_time;

    drop(task_inner);
    // ---- release current PCB

    // 把当前任务 push回全局的任务管理器
    add_task(task);

    // jump to scheduling cycle
    schedule(task_cx_ptr);
}

pub fn exit_current_and_run_next(exit_code: i32) {
    // 从 Process 管理器中 转移所有权的取出当前cpu执行的任务
    let task = take_current_task().unwrap();

    // 当前pid
    let pid = task.get_pid();

    // 如果是idle进程
    if pid == IDLE_PID {
        println!(
            "[kernel] Idle process exit with exit_code {} ...",
            exit_code
        );
        if exit_code != 0 {
            //crate::sbi::shutdown(255); //255 == -1 for err hint
            shutdown()
        } else {
            //crate::sbi::shutdown(0); //0 for success hint
            shutdown()
        }
    }

    // 改变pcb 的状态, 从当前task获取inner, 也就是当前当前pcb需要变化的值
    let mut inner = task.inner_exclusive_access();
    // 将状态更改为 僵尸进程, 供后续父进程查找回收资源
    inner.task_status = TaskStatus::Zombie;
    // 写入退出码
    inner.exit_code = exit_code;
    // 不需要修改父进程, 因为所有进程的父进程这里都为 initproc

    // 把当前进程, 所有的子进程, 都挂载到 initproc进程
    {
        let mut initproc_inner = INITPROC.inner_exclusive_access();
        for child in inner.children.iter() {
            child.inner_exclusive_access().parent = Some(Arc::downgrade(&INITPROC));  // 父进程 一个弱引用
            initproc_inner.children.push(child.clone());  // 挂载到initproc进程的childre中, 引用计数 +1, 现在是2
        }
    }

    inner.children.clear();  // 子进程引用计数-1 现在又变成1了

    // 统计内核时间
    let kernel_time = refresh_stop_clock();
    inner.kernel_time += kernel_time;
    println!("[kernel] user_time: {}ms, kernel_time: {}ms", inner.user_time /1000, inner.kernel_time / 1000);


    // 释放当前地址空间中, 逻辑段清空, areas->MapArea->data_frames->FrameTracker 执行析构, 过程是把areas中的物理页帧 根据 RAII还给操作系统
    // 注意 页表本身占用的 三级页表的物理帧(page_table->frames), 还没有被释放, 因为我们还需要使用, 这需要父进程管理进行释放
    inner.memory_set.recycle_data_pages();
    drop(inner);


    // 手动删除tcb, 后面这里永远不会执行了, 我们智能指针需要手动释放(这里需要给引用计数-1)
    drop(task);

    // 当前task 函数 为什么在 recycle_data_pages 之后还能执行? 函数栈帧为什么还能使用? 是因为我们虽然把 应用程序锁使用的逻辑页帧都取消映射了, 但是关于 task中的 pid 和kernel stack 还没有被释放
    // task 他在被fork的时候 parent_inner.children.push(task_control_block.clone()); 也clone了自己一份到父进程中, 所以这里删除drop只是删除了当前 Process中管理的引用计数(后续我们不会执行到这里了, 需要手动删除)

    // 这个进程不会被执行了, 我们当前栈帧随便创建一个上下文就好了
    let mut _unused = TaskContext::new();

    // 输出一下
    schedule(&mut _unused as *mut _);
}
