use core::sync::atomic::{AtomicBool, Ordering};

use crate::{
    arch::syscall::nr::*,
    process::{ProcessFlags, ProcessManager},
    sched::{schedule, SchedMode},
    syscall::user_access::check_and_clone_cstr,
};

use log::{info, warn};
use system_error::SystemError;
use table::{syscall_table, syscall_table_init};

use crate::arch::interrupt::TrapFrame;

use self::user_access::UserBufferWriter;

pub mod misc;
mod sys_getrandom;
mod sys_sysinfo;
pub mod table;
pub mod user_access;
pub mod user_buffer;

// 与linux不一致的调用，在linux基础上累加
pub const SYS_PUT_STRING: usize = 100000;
pub const SYS_SBRK: usize = 100001;
/// todo: 该系统调用与Linux不一致，将来需要删除该系统调用！！！ 删的时候记得改C版本的libc
pub const SYS_CLOCK: usize = 100002;
pub const SYS_SCHED: usize = 100003;

#[derive(Debug)]
pub struct Syscall;

impl Syscall {
    /// 初始化系统调用
    #[inline(never)]
    pub fn init() -> Result<(), SystemError> {
        static INIT_FLAG: AtomicBool = AtomicBool::new(false);
        let prev = INIT_FLAG.swap(true, Ordering::SeqCst);
        if prev {
            panic!("Cannot initialize syscall more than once!");
        }
        info!("Initializing syscall...");
        let r = crate::arch::syscall::arch_syscall_init();
        info!("Syscall init successfully!");

        return r;
    }
    /// 系统调用分发器，用于分发系统调用。
    ///
    /// 与[handle]不同，这个函数会捕获系统调用处理函数的panic，返回错误码。
    #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
    pub fn catch_handle(
        syscall_num: usize,
        args: &[usize],
        frame: &mut TrapFrame,
    ) -> Result<usize, SystemError> {
        use crate::debug::panic::kernel_catch_unwind;
        let res = kernel_catch_unwind(|| Self::handle(syscall_num, args, frame))?;
        res
    }
    /// @brief 系统调用分发器，用于分发系统调用。
    ///
    /// 这个函数内，需要根据系统调用号，调用对应的系统调用处理函数。
    /// 并且，对于用户态传入的指针参数，需要在本函数内进行越界检查，防止访问到内核空间。
    #[inline(never)]
    pub fn handle(
        syscall_num: usize,
        args: &[usize],
        frame: &mut TrapFrame,
    ) -> Result<usize, SystemError> {
        let mut seccomp_args = [args[0], args[1], args[2], args[3], args[4], args[5]];
        let current_pcb = ProcessManager::current_pcb();
        let nr = syscall_num as u64;
        let skip = current_pcb.ptrace_report_syscall(true, nr, &seccomp_args);
        if skip {
            // SYSEMU: skip the real syscall, return the current return register.
            return Ok(frame.get_syscall_return());
        }
        // The ptrace syscall-enter-stop may have rewritten the syscall number/args; re-fetch them from the frame.
        let nr_raw = frame.get_orig_syscall_nr();
        if nr_raw == -1 {
            // Emit a matching exit-stop to pair with the entry-stop that already
            // happened, then skip execution.
            let _ = current_pcb.ptrace_report_syscall(false, nr_raw as u64, &seccomp_args);
            return Ok(frame.get_syscall_return());
        }
        let mut dispatch_nr = nr_raw as usize;
        let mut dispatch_args = crate::process::seccomp::frame_syscall_args(frame);
        seccomp_args = dispatch_args;
        let mut seccomp_skipped = false;
        match crate::process::seccomp::secure_computing(dispatch_nr, &seccomp_args, frame) {
            Ok(crate::process::seccomp::SeccompDecision::Allow) => {}
            Ok(crate::process::seccomp::SeccompDecision::AllowAfterTrace) => {
                // Linux reloads the syscall number after entry work because
                // SECCOMP_RET_TRACE lets the tracer rewrite the register frame.
                // DragonOS dispatch also carries an explicit argument array,
                // so refresh all six arguments from that same frame snapshot.
                let nr_after_trace = frame.get_orig_syscall_nr();
                debug_assert!(
                    nr_after_trace >= 0,
                    "seccomp TRACE returned Allow with a negative syscall number"
                );
                dispatch_nr = nr_after_trace as usize;
                dispatch_args = crate::process::seccomp::frame_syscall_args(frame);
                seccomp_args = dispatch_args;
            }
            Ok(crate::process::seccomp::SeccompDecision::Skip(ret)) => {
                frame.set_return_value(ret);
                seccomp_skipped = true;
            }
            Err(e) => {
                frame.set_return_value(e.to_posix_errno() as i64 as u64 as usize);
                seccomp_skipped = true;
            }
        }

        defer::defer!({
            if ProcessManager::current_pcb()
                .flags()
                .contains(ProcessFlags::NEED_SCHEDULE)
            {
                schedule(SchedMode::SM_PREEMPT);
            }
        });

        // Execute the real syscall when seccomp did not intercept it; skip execution when it did.
        if !seccomp_skipped {
            // First try to obtain the handler from the syscall_table
            if let Some(handler) = syscall_table().get(dispatch_nr) {
                let show = false;
                if show {
                    log::debug!(
                        "pid: {} Syscall {} called with args {}",
                        ProcessManager::current_pid().data(),
                        handler.name,
                        handler.args_string(&dispatch_args)
                    );
                }

                let r = handler.inner_handle.handle(&dispatch_args, frame);
                if show {
                    log::debug!(
                        "pid: {} Syscall {} returned {:?}",
                        ProcessManager::current_pid().data(),
                        handler.name,
                        r
                    );
                }

                // Write the final return value into the return-value register
                let rax_value: usize = match r {
                    Ok(v) => v,
                    Err(e) => e.to_posix_errno() as i64 as u64 as usize,
                };
                frame.set_return_value(rax_value);
            } else {
                // fallback: an unregistered or unknown syscall.
                let r = match dispatch_nr {
                    SYS_PUT_STRING => Self::put_string(
                        dispatch_args[0] as *const u8,
                        dispatch_args[1] as u32,
                        dispatch_args[2] as u32,
                    ),
                    SYS_SBRK => {
                        let incr = dispatch_args[0] as isize;
                        crate::mm::syscall::sys_sbrk::sys_sbrk(incr)
                    }
                    SYS_CLOCK => Self::clock(),
                    SYS_SCHED => {
                        warn!("syscall sched");
                        schedule(SchedMode::SM_NONE);
                        Ok(0)
                    }
                    SYS_SYSLOG => {
                        let syslog_action_type = dispatch_args[0];
                        let buf_vaddr = dispatch_args[1];
                        let len = dispatch_args[2];
                        let from_user = frame.is_from_user();
                        if len == 0 {
                            Self::do_syslog(syslog_action_type, &mut [], 0)
                        } else {
                            match UserBufferWriter::new(buf_vaddr as *mut u8, len, from_user) {
                                Ok(mut writer) => match writer.buffer(0) {
                                    Ok(buf) => Self::do_syslog(syslog_action_type, buf, len),
                                    Err(e) => Err(e),
                                },
                                Err(e) => Err(e),
                            }
                        }
                    }
                    SYS_FSYNC => {
                        warn!("SYS_FSYNC has not yet been implemented");
                        Ok(0)
                    }
                    _ => {
                        log::error!(
                            "Unsupported syscall ID: {} -> {}, args: {:?}",
                            dispatch_nr,
                            syscall_number_to_str(dispatch_nr),
                            dispatch_args
                        );
                        Err(SystemError::ENOSYS)
                    }
                };
                let rax_value: usize = match &r {
                    Ok(v) => *v,
                    Err(e) => e.to_posix_errno() as i64 as u64 as usize,
                };
                frame.set_return_value(rax_value);
            }
        }

        // Unified ptrace syscall-exit-stop: every path must go through exit-stop.
        let current_pcb = ProcessManager::current_pcb();
        let _ = current_pcb.ptrace_report_syscall(false, dispatch_nr as u64, &seccomp_args);

        // Return the return-value register (ptrace may have rewritten it via POKEUSER);
        return Ok(frame.get_syscall_return());
    }

    pub fn put_string(
        s: *const u8,
        front_color: u32,
        back_color: u32,
    ) -> Result<usize, SystemError> {
        // todo: 删除这个系统调用
        let s = check_and_clone_cstr(s, Some(4096))?
            .into_string()
            .map_err(|_| SystemError::EINVAL)?;
        let fr = (front_color & 0x00ff0000) >> 16;
        let fg = (front_color & 0x0000ff00) >> 8;
        let fb = front_color & 0x000000ff;
        let br = (back_color & 0x00ff0000) >> 16;
        let bg = (back_color & 0x0000ff00) >> 8;
        let bb = back_color & 0x000000ff;
        print!("\x1B[38;2;{fr};{fg};{fb};48;2;{br};{bg};{bb}m{s}\x1B[0m");
        return Ok(s.len());
    }
}

#[inline(never)]
pub fn syscall_init() -> Result<(), SystemError> {
    syscall_table_init()?;
    Ok(())
}
