use core::arch::asm;

use crate::config::*;
use crate::trap::TrapContext;

static KERNEL_STACK: [[u8; KERNEL_STACK_SIZE]; MAX_APP_NUM] = [[0; KERNEL_STACK_SIZE]; MAX_APP_NUM];  // 这一章, 每个用户应用对应一个内核栈 [每个应用自己的内核栈; 总应用数]
static USER_STACK: [[u8; USER_STACK_SIZE]; MAX_APP_NUM] = [[0; USER_STACK_SIZE]; MAX_APP_NUM];  // 这一行每个用户应用对应一个应用栈

extern "C" {
    fn _num_app();
}

// 得到用户app的数量
pub fn get_num_app() -> usize{
    unsafe{
        (_num_app as usize as *const usize).read_volatile()
    }
}

// 把 app一次性都加载到内存指定位置
pub fn load_app() {
    // 得到符号位
    let num_app_ptr = _num_app as usize as *const usize;

    // 得到 符号开始的前8个字节, 这里保存的app的数量
    let num_app = get_num_app();

    // 得到 app数组的起始位置 num_app_ptr.add(1)跳过上方的 metadata, num_app+1 是确保切免得长度足够, 因为后面还有一个符号在linker.ld中  .quad app_2_end 表示结束的内存地址
    let app_start = unsafe {
        core::slice::from_raw_parts(num_app_ptr.add(1), num_app+1)
    };

    // 清除缓存
    unsafe { asm!("fence.i"); }

    // 加载app
    for app_id in 0..num_app {
        // 得到每个app的起始位置
        let base_ptr = APP_BASE_ADDRESS + app_id * APP_SIZE_LIMIT;

        // 清理这个应用可以占用的内存(好像可以不用做吧? 下面直接dst.copy_from_slice全部覆盖了)
        (base_ptr..base_ptr + APP_SIZE_LIMIT).for_each(|addr| unsafe {
            (addr as *mut u8).write_volatile(0)
        });

        // 加载 app_start 处二进制数据到内存里
        let src = unsafe {
            let app_size = app_start[app_id + 1] - app_start[app_id];  // 下一个app的起始位置, 减去当前的起始位置就是app的大小
            core::slice::from_raw_parts(app_start[app_id] as *const u8, app_size)
        };

        // 得到app占用的内存, 后面需要把app 二进制数据加载到这里
        let dst = unsafe {
            core::slice::from_raw_parts_mut(base_ptr as *mut u8, src.len())
        };
        dst.copy_from_slice(src);
    }
}


// 初始化用户应用栈, 并返回用户应用栈在内核栈中的trap_context的地址
// 这个函数根据 app_id得到 USER_STACK的所在的位置, 初始化之后, 再构造 KERNEL_STACK
pub fn init_app_cx(app_id: usize) -> usize {
    // 得到 用户栈
    unsafe {
        // 构造 trap_context
        let user_trap_context = {
            // 初始化用户栈顶
            let user_stack_top = {
                let _tmp_stack = USER_STACK.as_ptr() as usize + USER_STACK_SIZE * app_id;  // 用户栈开始 + (每个用户栈的大小 * 指定用户栈的索引) 得到栈顶位置,
                _tmp_stack + USER_STACK_SIZE  // 加上栈大小, 得到新的栈顶位置
            };

            // 用户应用的开始位置
            let user_entry = APP_BASE_ADDRESS + APP_SIZE_LIMIT * app_id;

            TrapContext::from(user_entry, user_stack_top)
        };

        // 把构造的 user_trap_context 放到 KERNEL_STACK指定应用的内核栈中
        let kernel_app_stack_ptr = {
            // 栈顶位置 + 栈大小 = 栈底
            let _tme_stack_low = KERNEL_STACK.as_ptr() as usize + KERNEL_STACK_SIZE * app_id + KERNEL_STACK_SIZE;

            // 给TrapContext 分配栈空间, 得到新的栈顶
            (_tme_stack_low - core::mem::size_of::<TrapContext>()) as * mut TrapContext
        };

        // copy 数据到内核栈中
        *kernel_app_stack_ptr = user_trap_context;

        // 返回内核栈的地址
        kernel_app_stack_ptr as usize
    }
}