use alloc::{string::String, sync::Arc, vec::Vec};
use system_error::SystemError;

use crate::{
    arch::{
        interrupt::TrapFrame,
        process::table::{USER_CS, USER_DS},
        CurrentIrqArch,
    },
    exception::InterruptArch,
    mm::ucontext::AddressSpace,
    process::{
        exec::{load_binary_file, ExecParam, ExecParamFlags},
        ProcessControlBlock, ProcessManager,
    },
    syscall::{user_access::UserBufferWriter, Syscall},
};

impl Syscall {
    pub fn do_execve(
        path: String,
        argv: Vec<String>,
        envp: Vec<String>,
        regs: &mut TrapFrame,
    ) -> Result<(), SystemError> {
        // 关中断，防止在设置地址空间的时候，发生中断，然后进调度器，出现错误。
        let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() };
        let pcb = ProcessManager::current_pcb();
        // crate::kdebug!(
        //     "pid: {:?}  do_execve: path: {:?}, argv: {:?}, envp: {:?}\n",
        //     pcb.pid(),
        //     path,
        //     argv,
        //     envp
        // );

        let mut basic_info = pcb.basic_mut();
        // 暂存原本的用户地址空间的引用(因为如果在切换页表之前释放了它，可能会造成内存use after free)
        let old_address_space = basic_info.user_vm();

        // 在pcb中原来的用户地址空间
        unsafe {
            basic_info.set_user_vm(None);
        }
        // 创建新的地址空间并设置为当前地址空间
        let address_space = AddressSpace::new(true).expect("Failed to create new address space");
        unsafe {
            basic_info.set_user_vm(Some(address_space.clone()));
        }

        // to avoid deadlock
        drop(basic_info);

        assert!(
            AddressSpace::is_current(&address_space),
            "Failed to set address space"
        );
        // kdebug!("Switch to new address space");

        // 切换到新的用户地址空间
        unsafe { address_space.read().user_mapper.utable.make_current() };

        drop(old_address_space);
        drop(irq_guard);
        // kdebug!("to load binary file");
        let mut param = ExecParam::new(path.as_str(), address_space.clone(), ExecParamFlags::EXEC)?;

        // 加载可执行文件
        let load_result = load_binary_file(&mut param)
            .unwrap_or_else(|e| panic!("Failed to load binary file: {:?}, path: {:?}", e, path));
        // kdebug!("load binary file done");
        // kdebug!("argv: {:?}, envp: {:?}", argv, envp);
        param.init_info_mut().args = argv;
        param.init_info_mut().envs = envp;

        // 把proc_init_info写到用户栈上

        let (user_sp, argv_ptr) = unsafe {
            param
                .init_info()
                .push_at(
                    address_space
                        .write()
                        .user_stack_mut()
                        .expect("No user stack found"),
                )
                .expect("Failed to push proc_init_info to user stack")
        };

        // kdebug!("write proc_init_info to user stack done");

        // （兼容旧版libc）把argv的指针写到寄存器内
        // TODO: 改写旧版libc，不再需要这个兼容
        regs.rdi = param.init_info().args.len() as u64;
        regs.rsi = argv_ptr.data() as u64;

        // 设置系统调用返回时的寄存器状态
        // TODO: 中断管理重构后，这里的寄存器状态设置要删掉！！！改为对trap frame的设置。要增加架构抽象。
        regs.rsp = user_sp.data() as u64;
        regs.rbp = user_sp.data() as u64;
        regs.rip = load_result.entry_point().data() as u64;

        regs.cs = USER_CS.bits() as u64;
        regs.ds = USER_DS.bits() as u64;
        regs.ss = USER_DS.bits() as u64;
        regs.es = 0;
        regs.rflags = 0x200;
        regs.rax = 1;

        drop(param);

        // kdebug!("regs: {:?}\n", regs);

        // crate::kdebug!(
        //     "tmp_rs_execve: done, load_result.entry_point()={:?}",
        //     load_result.entry_point()
        // );

        return Ok(());
    }

    /// ## 用于控制和查询与体系结构相关的进程特定选项
    pub fn arch_prctl(option: usize, arg2: usize) -> Result<usize, SystemError> {
        let pcb = ProcessManager::current_pcb();
        if let Err(SystemError::EINVAL) = Self::do_arch_prctl_64(&pcb, option, arg2, true) {
            Self::do_arch_prctl_common(option, arg2)?;
        }
        Ok(0)
    }

    /// ## 64位下控制fs/gs base寄存器的方法
    pub fn do_arch_prctl_64(
        pcb: &Arc<ProcessControlBlock>,
        option: usize,
        arg2: usize,
        from_user: bool,
    ) -> Result<usize, SystemError> {
        let mut arch_info = pcb.arch_info_irqsave();
        match option {
            ARCH_GET_FS => {
                unsafe { arch_info.save_fsbase() };
                let mut writer = UserBufferWriter::new(
                    arg2 as *mut usize,
                    core::mem::size_of::<usize>(),
                    from_user,
                )?;
                writer.copy_one_to_user(&arch_info.fsbase, 0)?;
            }
            ARCH_GET_GS => {
                unsafe { arch_info.save_gsbase() };
                let mut writer = UserBufferWriter::new(
                    arg2 as *mut usize,
                    core::mem::size_of::<usize>(),
                    from_user,
                )?;
                writer.copy_one_to_user(&arch_info.gsbase, 0)?;
            }
            ARCH_SET_FS => {
                arch_info.fsbase = arg2;
                // 如果是当前进程则直接写入寄存器
                if pcb.pid() == ProcessManager::current_pcb().pid() {
                    unsafe { arch_info.restore_fsbase() }
                }
            }
            ARCH_SET_GS => {
                arch_info.gsbase = arg2;
                if pcb.pid() == ProcessManager::current_pcb().pid() {
                    unsafe { arch_info.restore_gsbase() }
                }
            }
            _ => {
                return Err(SystemError::EINVAL);
            }
        }
        Ok(0)
    }

    #[allow(dead_code)]
    pub fn do_arch_prctl_common(_option: usize, _arg2: usize) -> Result<usize, SystemError> {
        todo!("do_arch_prctl_common not unimplemented");
    }
}

pub const ARCH_SET_GS: usize = 0x1001;
pub const ARCH_SET_FS: usize = 0x1002;
pub const ARCH_GET_FS: usize = 0x1003;
pub const ARCH_GET_GS: usize = 0x1004;
