//! App management syscalls
use alloc::sync::Arc;
use crate::loader::get_app_data_by_name;
use crate::mm::page_table::{translated_refmut, translated_str};
// use crate::batch::run_next_app;
use crate::println;
use crate::task::{exit_current_and_run_next, suspend_current_and_run_next};
use crate::task::manager::add_task;
use crate::task::processor::{change_program_brk, current_task, current_user_token};
use crate::task::task::TaskStatus;
// use crate::task::{change_program_brk, exit_current_and_run_next, suspend_current_and_run_next};
use crate::timer::get_time_ms;

/// 任务退出, 并立即切换任务
pub fn sys_exit(exit_code: i32) -> ! {
    println!("[kernel] Application exited with code {}", exit_code);
    exit_current_and_run_next(exit_code);
    panic!("Unreachable in sys_exit!");
}

pub fn sys_yield() -> isize {
    suspend_current_and_run_next();
    0
}

pub fn sys_get_time() -> isize {
    get_time_ms() as isize
}

// 根据传入的 size 对当前堆进行扩容和缩小, 返回值为 失败 或者 申请的空间的起始位置
pub fn sys_sbrk(size: i32) -> isize {
    if let Some(old_brk) = change_program_brk(size) {
        old_brk as isize
    } else {
        -1
    }
}

pub fn sys_getpid() -> isize {
    current_task().unwrap().pid.0 as isize
}

pub fn sys_fork() -> isize {
    // 创建一个会 跳到trap_return的子进程
    let current_task = current_task().unwrap();
    let new_task = current_task.fork();
    let new_pid = new_task.pid.0;

    // 修改子进程 trap_context 中的返回值, 因为父进程在调用sys_call中之后会立即返回
    let trap_cx = new_task.inner_exclusive_access().get_trap_cx();
    // 我们之前 进入这个函数的时候, 已经对子进程的 结束陷入的进行返回的地址 +4了
    // 所以现在只需要改变子进程 陷入之后的返回值即可, 将来子进程会直接从 trap_return运行
    trap_cx.x[10] = 0;
    // 添加子任务到全局的 任务管理器
    add_task(new_task);

    // 返回 子进程的pid 给父进程
    new_pid as isize
}

// 根据 文件名, 创建替换当前执行的任务
pub fn sys_exec(path: *const u8) -> isize {
    // 当前token
    let token = current_user_token();
    // 得到 文件名
    let path = translated_str(token, path);
    // 根据文件名对应的二进制数据, 调用exec 替换
    if let Some(data) = get_app_data_by_name(path.as_str()) {
        let task = current_task().unwrap();
        task.exec(data);
        0
    } else {
        // 不存在 返回-1
        -1
    }
}

/// pid分为如下几种情况:
///
/// - -1 表示等待任意进程结束
///
/// - 1235 表示只等待pid为1234的进程
///
/// 返回值会返回如下:
///
/// - -1 表示要等待的进程不是-1(任意进程)是指定进程, 但是指定进程并没有在children中
///
/// - -2 表示目前还没有僵尸进程
///
/// - pid 返回被回收的pid
///

pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize {
    // 当前进程 这里是父进程
    let task = current_task().unwrap();
    // find a child process

    // 如果当前进程内没有对应pid的子进程 则返回-1, 如果传进来的pid是-1, 则表示任何一个子进程都是符合要求的
    let mut inner = task.inner_exclusive_access();
    if !inner
        .children
        .iter()
        .any(|p| pid == -1 || pid as usize == p.get_pid())
    {
        return -1;
        // ---- release current PCB
    }

    // 找到 指定进程且进程为 僵尸进程或者是任意进程
    let pair = inner.children.iter().enumerate().find(|(_, p)| {
        // ++++ temporarily access child PCB lock exclusively
        p.inner_exclusive_access().task_status == TaskStatus::Zombie && (pid == -1 || pid as usize == p.get_pid())
        // ++++ release child PCB
    });

    // 如果找到
    if let Some((idx, _)) = pair {
        // 从子列表中删除并取出
        let child = inner.children.remove(idx);
        // confirm that child will be deallocated after removing from children list
        // 保证chlid当前是唯一存在的引用, 而不会出现在某个进程的子进程向量中, 更不会出现在处理器监控器或者任务管理器中, child在当前作用于结束后引用计数为0, 彻底删除,
        // 作用域结束后, 内部的pid 和kernel stack 执行他们自己的drop方法(返还pid给pid管理器, 返还kernel stack的资源还给 KERNEL_SPACE, ), 还有 inner->memory_set->page_table->frames 三级页表本身所占的物理帧

        assert_eq!(Arc::strong_count(&child), 1);
        let found_pid = child.get_pid();
        // ++++ temporarily access child TCB exclusively
        // 找到子进程的退出码
        let exit_code = child.inner_exclusive_access().exit_code;
        // ++++ release child PCB

        // 将退出码, 写入到当前进程的地址空间
        let exit_code_ref = translated_refmut(inner.memory_set.token(), exit_code_ptr);
        *exit_code_ref = exit_code;

        // 返回回收的子进程的pid
        found_pid as isize
    } else {
        // 如果制定进程不是僵尸进程 返回-2, 上层用户应用调用的 wait pid 会继续等待
        -2
    }
    // ---- release current PCB lock automatically
}