use crate::arch::interrupt::TrapFrame;
use crate::{
    arch::syscall::nr::SYS_PIPE2,
    filesystem::vfs::file::{File, FileFlags},
    ipc::pipe::LockedPipeInode,
    process::ProcessManager,
    syscall::{
        table::{FormattedSyscallParam, Syscall},
        user_access::UserBufferWriter,
    },
};
use alloc::vec::Vec;
use core::ffi::c_int;
use system_error::SystemError;
pub struct SysPipe2Handle;

// Extracted core logic for pipe2
// pub(super) makes it visible to other modules in kernel/src/ipc/syscall/
pub(super) fn do_kernel_pipe2(fd: *mut i32, flags: FileFlags) -> Result<usize, SystemError> {
    if !flags
        .difference(FileFlags::O_CLOEXEC | FileFlags::O_NONBLOCK | FileFlags::O_DIRECT)
        .is_empty()
    {
        return Err(SystemError::EINVAL);
    }

    let mut user_buffer = UserBufferWriter::new(fd, core::mem::size_of::<[c_int; 2]>(), true)?;
    let fd = user_buffer.buffer::<i32>(0)?;
    let pipe_ptr = LockedPipeInode::new();

    let read_file = File::new(
        pipe_ptr.clone(),
        FileFlags::O_RDONLY | (flags & FileFlags::O_NONBLOCK),
    )?;

    let write_file = File::new(
        pipe_ptr.clone(),
        FileFlags::O_WRONLY | (flags & (FileFlags::O_NONBLOCK | FileFlags::O_DIRECT)),
    )?;

    let cloexec = flags.contains(FileFlags::O_CLOEXEC);
    let fd_table_ptr = ProcessManager::current_pcb().fd_table();
    let mut fd_table_guard = fd_table_ptr.write();
    let read_fd = fd_table_guard.alloc_fd(read_file, None, cloexec)?;
    let write_fd = fd_table_guard.alloc_fd(write_file, None, cloexec)?;

    drop(fd_table_guard);

    fd[0] = read_fd;
    fd[1] = write_fd;
    Ok(0)
}

impl SysPipe2Handle {
    #[inline(always)]
    fn pipefd(args: &[usize]) -> *mut i32 {
        args[0] as *mut c_int
    }
    #[inline(always)]
    fn flags(args: &[usize]) -> FileFlags {
        FileFlags::from_bits_truncate(args[1] as u32)
    }
}

impl Syscall for SysPipe2Handle {
    fn num_args(&self) -> usize {
        2 // fd_ptr, flags
    }

    fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result<usize, SystemError> {
        let fd_ptr = Self::pipefd(args);
        if fd_ptr.is_null() {
            return Err(SystemError::EFAULT);
        } else {
            let flags = FileFlags::from_bits_truncate(args[1] as u32);
            do_kernel_pipe2(fd_ptr, flags)
        }
    }

    fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam> {
        let fd_ptr = Self::pipefd(args);
        vec![
            FormattedSyscallParam::new("fd_ptr", format!("{}", fd_ptr as usize)), // Format pointer as hex
            FormattedSyscallParam::new("flags", format!("{}", Self::flags(args).bits())),
        ]
    }
}

syscall_table_macros::declare_syscall!(SYS_PIPE2, SysPipe2Handle);
