use crate::arch::SyscallArgType;
use libc::{c_ulonglong, user_regs_struct};
use std::ops::Index;
use syscalls::aarch64::Sysno;
use syscalls::SysnoSet;

#[allow(clippy::enum_glob_use)]
use syscalls::aarch64::Sysno::*;

pub static TRACE_DESC: SysnoSet = SysnoSet::new(&[
    // strace/src/linux/64/syscallent.h
    fsetxattr,
    fgetxattr,
    flistxattr,
    fremovexattr,
    eventfd2,
    epoll_create1,
    epoll_ctl,
    epoll_pwait,
    dup,
    dup3,
    fcntl,
    inotify_init1,
    inotify_add_watch,
    inotify_rm_watch,
    ioctl,
    flock,
    mknodat,
    mkdirat,
    unlinkat,
    symlinkat,
    linkat,
    renameat,
    fstatfs,
    ftruncate,
    fallocate,
    faccessat,
    fchdir,
    fchmod,
    fchmodat,
    fchownat,
    fchown,
    openat,
    close,
    pipe2,
    getdents64,
    lseek,
    read,
    write,
    readv,
    writev,
    pread64,
    pwrite64,
    preadv,
    pwritev,
    sendfile,
    pselect6,
    ppoll,
    signalfd4,
    vmsplice,
    splice,
    tee,
    readlinkat,
    fstatat,
    fstat,
    fsync,
    fdatasync,
    sync_file_range,
    timerfd_create,
    timerfd_settime,
    timerfd_gettime,
    utimensat,
    mq_open,
    mq_timedsend,
    mq_timedreceive,
    mq_notify,
    mq_getsetattr,
    readahead,
    mmap,
    fadvise64,
    perf_event_open,
    fanotify_init,
    fanotify_mark,
    name_to_handle_at,
    open_by_handle_at,
    syncfs,
    setns,
    finit_module,
    renameat2,
    memfd_create,
    bpf,
    execveat,
    userfaultfd,
    copy_file_range,
    preadv2,
    pwritev2,
    statx,
    kexec_file_load,
    // strace/src/linux/generic/syscallent-common.h
    pidfd_send_signal,
    io_uring_setup,
    io_uring_enter,
    io_uring_register,
    open_tree,
    move_mount,
    fsopen,
    fsconfig,
    fsmount,
    fspick,
    pidfd_open,
    openat2,
    pidfd_getfd,
    faccessat2,
    process_madvise,
    epoll_pwait2,
    mount_setattr,
    quotactl_fd,
    landlock_create_ruleset,
    landlock_add_rule,
    landlock_restrict_self,
    memfd_secret,
    process_mrelease,
    cachestat,
    fchmodat2,
]);

pub static TRACE_FILE: SysnoSet = SysnoSet::new(&[
    // strace/src/linux/64/syscallent.h
    setxattr,
    lsetxattr,
    getxattr,
    lgetxattr,
    listxattr,
    llistxattr,
    removexattr,
    lremovexattr,
    getcwd,
    inotify_add_watch,
    mknodat,
    mkdirat,
    unlinkat,
    symlinkat,
    linkat,
    renameat,
    umount2,
    mount,
    pivot_root,
    statfs,
    fstatfs,
    truncate,
    faccessat,
    chdir,
    chroot,
    fchmodat,
    fchownat,
    openat,
    quotactl,
    readlinkat,
    fstatat,
    fstat,
    utimensat,
    acct,
    execve,
    swapon,
    swapoff,
    fanotify_mark,
    name_to_handle_at,
    renameat2,
    execveat,
    statx,
    // strace/src/linux/generic/syscallent-common.h
    open_tree,
    move_mount,
    fsconfig,
    fspick,
    openat2,
    faccessat2,
    mount_setattr,
    fchmodat2,
]);

pub static TRACE_IPC: SysnoSet = SysnoSet::new(&[
    msgget, msgctl, msgrcv, msgsnd, semget, semctl, semtimedop, semop, shmget, shmctl, shmat, shmdt,
]);

pub static TRACE_NETWORK: SysnoSet = SysnoSet::new(&[
    sendfile,
    socket,
    socketpair,
    bind,
    listen,
    accept,
    connect,
    getsockname,
    getpeername,
    sendto,
    recvfrom,
    setsockopt,
    getsockopt,
    shutdown,
    sendmsg,
    recvmsg,
    accept4,
    recvmmsg,
    sendmmsg,
]);

pub static TRACE_PROCESS: SysnoSet = SysnoSet::new(&[
    // strace/src/linux/64/syscallent.h
    exit,
    exit_group,
    waitid,
    kill,
    tkill,
    tgkill,
    rt_sigqueueinfo,
    clone,
    execve,
    rt_tgsigqueueinfo,
    wait4,
    execveat,
    // strace/src/linux/generic/syscallent-common.h
    pidfd_send_signal,
    clone3,
]);

pub static TRACE_SIGNAL: SysnoSet = SysnoSet::new(&[
    // strace/src/linux/64/syscallent.h
    statfs,
    fstatfs,
    signalfd4,
    fstatat,
    fstat,
    kill,
    tkill,
    tgkill,
    sigaltstack,
    rt_sigsuspend,
    rt_sigaction,
    rt_sigprocmask,
    rt_sigpending,
    rt_sigtimedwait,
    rt_sigqueueinfo,
    rt_sigreturn,
    execve,
    rt_tgsigqueueinfo,
    execveat,
    statx,
    // strace/src/linux/generic/syscallent-common.h
    pidfd_send_signal,
    io_uring_enter,
]);

pub static TRACE_MEMORY: SysnoSet = SysnoSet::new(&[
    // strace/src/linux/64/syscallent.h
    io_setup,
    io_destroy,
    shmat,
    shmdt,
    brk,
    munmap,
    mremap,
    mmap,
    mprotect,
    msync,
    mlock,
    munlock,
    mlockall,
    munlockall,
    mincore,
    madvise,
    remap_file_pages,
    mbind,
    get_mempolicy,
    set_mempolicy,
    migrate_pages,
    move_pages,
    mlock2,
    pkey_mprotect,
    // strace/src/linux/generic/syscallent-common.h
    io_uring_register,
    set_mempolicy_home_node,
    map_shadow_stack,
]);

pub static TRACE_STAT: SysnoSet = SysnoSet::new(&[fstatat, fstat, statx]);
pub static TRACE_LSTAT: SysnoSet = SysnoSet::new(&[]);
pub static TRACE_FSTAT: SysnoSet = SysnoSet::new(&[fstatat, fstat, statx]);
pub static TRACE_STAT_LIKE: SysnoSet = SysnoSet::new(&[fstatat, fstat, statx]);
pub static TRACE_STATFS: SysnoSet = SysnoSet::new(&[statfs, fstatfs]);
pub static TRACE_FSTATFS: SysnoSet = SysnoSet::new(&[fstatfs]);
pub static TRACE_STATFS_LIKE: SysnoSet = SysnoSet::new(&[statfs, fstatfs]);

pub static TRACE_PURE: SysnoSet =
    SysnoSet::new(&[getpid, getppid, getuid, geteuid, getgid, getegid, gettid]);

pub static TRACE_CREDS: SysnoSet = SysnoSet::new(&[
    capget,
    capset,
    clock_settime,
    clock_gettime,
    clock_getres,
    setregid,
    setgid,
    setreuid,
    setuid,
    setresuid,
    getresuid,
    setresgid,
    getresgid,
    setfsuid,
    setfsgid,
    getgroups,
    setgroups,
    prctl,
    gettimeofday,
    settimeofday,
    adjtimex,
    getuid,
    geteuid,
    getgid,
    getegid,
    clock_adjtime,
]);

pub static TRACE_CLOCK: SysnoSet = SysnoSet::new(&[
    clock_settime,
    clock_gettime,
    clock_getres,
    gettimeofday,
    settimeofday,
    adjtimex,
    clock_adjtime,
]);

macro_rules! syscall {
    ($name:ident $(,)?) => {
        Some((Sysno::$name, [None, None, None, None, None, None]))
    };
    ($name:ident, $arg0:ident $(,)?) => {
        Some((Sysno::$name, [$arg0, None, None, None, None, None]))
    };
    ($name:ident, $arg0:ident, $arg1:ident $(,)?) => {
        Some((Sysno::$name, [$arg0, $arg1, None, None, None, None]))
    };
    ($name:ident, $arg0:ident, $arg1:ident, $arg2:ident $(,)?) => {
        Some((Sysno::$name, [$arg0, $arg1, $arg2, None, None, None]))
    };
    ($name:ident, $arg0:ident, $arg1:ident, $arg2:ident, $arg3:ident $(,)?) => {
        Some((Sysno::$name, [$arg0, $arg1, $arg2, $arg3, None, None]))
    };
    ($name:ident, $arg0:ident, $arg1:ident, $arg2:ident, $arg3:ident, $arg4:ident $(,)?) => {
        Some((Sysno::$name, [$arg0, $arg1, $arg2, $arg3, $arg4, None]))
    };
    ($name:ident, $arg0:ident, $arg1:ident, $arg2:ident, $arg3:ident, $arg4:ident, $arg5:ident $(,)?) => {
        Some((Sysno::$name, [$arg0, $arg1, $arg2, $arg3, $arg4, $arg5]))
    };
}

const ADDR: Option<SyscallArgType> = Some(SyscallArgType::Addr);
const INT: Option<SyscallArgType> = Some(SyscallArgType::Int);
const STR: Option<SyscallArgType> = Some(SyscallArgType::Str);
const STRV: Option<SyscallArgType> = Some(SyscallArgType::StrArray);

pub struct Aarch64Syscalls {
    _0: [Option<(Sysno, [Option<SyscallArgType>; 6])>; 244],
    _260: [Option<(Sysno, [Option<SyscallArgType>; 6])>; 35],
    _424: [Option<(Sysno, [Option<SyscallArgType>; 6])>; 29],
}

impl Aarch64Syscalls {
    pub fn get(&self, index: usize) -> Option<&Option<(Sysno, [Option<SyscallArgType>; 6])>> {
        let result = match index {
            0..=243 => &self._0[index],
            260..=294 => &self._260[index - 260],
            424..=452 => &self._424[index - 424],
            _ => return None,
        };
        Some(result)
    }
}

impl Index<usize> for Aarch64Syscalls {
    type Output = Option<(Sysno, [Option<SyscallArgType>; 6])>;

    fn index(&self, index: usize) -> &Self::Output {
        self.get(index).expect("unimplemented syscall")
    }
}

pub static SYSCALLS: Aarch64Syscalls = Aarch64Syscalls {
    _0: [
        syscall!(io_setup, INT, ADDR),
        syscall!(io_destroy, INT),
        syscall!(io_submit, INT, INT, ADDR),
        syscall!(io_cancel, INT, ADDR, ADDR),
        syscall!(io_getevents, INT, INT, INT, ADDR, ADDR),
        syscall!(setxattr, STR, STR, ADDR, INT, INT),
        syscall!(lsetxattr, STR, STR, ADDR, INT, INT),
        syscall!(fsetxattr, INT, STR, ADDR, INT, INT),
        syscall!(getxattr, STR, STR, ADDR, INT),
        syscall!(lgetxattr, STR, STR, ADDR, INT),
        syscall!(fgetxattr, INT, STR, ADDR, INT),
        syscall!(listxattr, STR, STR, INT),
        syscall!(llistxattr, STR, STR, INT),
        syscall!(flistxattr, INT, STR, INT),
        syscall!(removexattr, STR, STR),
        syscall!(lremovexattr, STR, STR),
        syscall!(fremovexattr, INT, STR),
        syscall!(getcwd, STR, INT),
        syscall!(lookup_dcookie, INT, STR, INT),
        syscall!(eventfd2, INT, INT),
        syscall!(epoll_create1, INT),
        syscall!(epoll_ctl, INT, INT, INT, ADDR),
        syscall!(epoll_pwait, INT, ADDR, INT, INT, ADDR, INT),
        syscall!(dup, INT),
        syscall!(dup3, INT, INT, INT),
        syscall!(fcntl, INT, INT, INT),
        syscall!(inotify_init1, INT),
        syscall!(inotify_add_watch, INT, STR, INT),
        syscall!(inotify_rm_watch, INT, INT),
        syscall!(ioctl, INT, INT, INT),
        syscall!(ioprio_set, INT, INT, INT),
        syscall!(ioprio_get, INT, INT),
        syscall!(flock, INT, INT),
        syscall!(mknodat, INT, STR, INT, INT),
        syscall!(mkdirat, INT, STR, INT),
        syscall!(unlinkat, INT, STR, INT),
        syscall!(symlinkat, STR, INT, STR),
        syscall!(linkat, INT, STR, INT, STR, INT),
        syscall!(renameat, INT, STR, INT, STR),
        syscall!(umount2, STR, INT),
        syscall!(mount, STR, STR, STR, INT, ADDR),
        syscall!(pivot_root, STR, STR),
        syscall!(nfsservctl, INT, ADDR, ADDR),
        syscall!(statfs, STR, ADDR),
        syscall!(fstatfs, INT, ADDR),
        syscall!(truncate, STR, INT),
        syscall!(ftruncate, INT, INT),
        syscall!(fallocate, INT, INT, INT, INT),
        syscall!(faccessat, INT, STR, INT),
        syscall!(chdir, STR),
        syscall!(fchdir, INT),
        syscall!(chroot, STR),
        syscall!(fchmod, INT, INT),
        syscall!(fchmodat, INT, STR, INT),
        syscall!(fchownat, INT, STR, INT, INT, INT),
        syscall!(fchown, INT, INT, INT),
        syscall!(openat, INT, STR, INT, INT),
        syscall!(close, INT),
        syscall!(vhangup),
        syscall!(pipe2, ADDR, INT),
        syscall!(quotactl, INT, STR, INT, ADDR),
        syscall!(getdents64, INT, ADDR, INT),
        syscall!(lseek, INT, INT, INT),
        syscall!(read, INT, STR, INT),
        syscall!(write, INT, STR, INT),
        syscall!(readv, INT, ADDR, INT),
        syscall!(writev, INT, ADDR, INT),
        syscall!(pread64, INT, STR, INT, INT),
        syscall!(pwrite64, INT, STR, INT, INT),
        syscall!(preadv, INT, ADDR, INT, INT, INT),
        syscall!(pwritev, INT, ADDR, INT, INT, INT),
        syscall!(sendfile, INT, INT, ADDR, INT),
        syscall!(pselect6, INT, ADDR, ADDR, ADDR, ADDR, ADDR),
        syscall!(ppoll, ADDR, INT, ADDR, ADDR, INT),
        syscall!(signalfd4, INT, ADDR, INT, INT),
        syscall!(vmsplice, INT, ADDR, INT, INT),
        syscall!(splice, INT, ADDR, INT, ADDR, INT, INT),
        syscall!(tee, INT, INT, INT, INT),
        syscall!(readlinkat, INT, STR, STR, INT),
        syscall!(fstatat, INT, STR, ADDR, INT),
        syscall!(fstat, INT, ADDR),
        syscall!(sync),
        syscall!(fsync, INT),
        syscall!(fdatasync, INT),
        syscall!(sync_file_range, INT, INT, INT, INT),
        syscall!(timerfd_create, INT, INT),
        syscall!(timerfd_settime, INT, INT, ADDR, ADDR),
        syscall!(timerfd_gettime, INT, ADDR),
        syscall!(utimensat, INT, STR, ADDR, INT),
        syscall!(acct, STR),
        syscall!(capget, ADDR, ADDR),
        syscall!(capset, ADDR, ADDR),
        syscall!(personality, INT),
        syscall!(exit, INT),
        syscall!(exit_group, INT),
        syscall!(waitid, INT, INT, ADDR, INT, ADDR),
        syscall!(set_tid_address, ADDR),
        syscall!(unshare, INT),
        syscall!(futex, ADDR, INT, INT, ADDR, ADDR, INT),
        syscall!(set_robust_list, ADDR, INT),
        syscall!(get_robust_list, INT, ADDR, ADDR),
        syscall!(nanosleep, ADDR, ADDR),
        syscall!(getitimer, INT, ADDR),
        syscall!(setitimer, INT, ADDR, ADDR),
        syscall!(kexec_load, INT, INT, ADDR, INT),
        syscall!(init_module, ADDR, INT, STR),
        syscall!(delete_module, STR, INT),
        syscall!(timer_create, INT, ADDR, ADDR),
        syscall!(timer_gettime, INT, ADDR),
        syscall!(timer_getoverrun, INT),
        syscall!(timer_settime, INT, INT, ADDR, ADDR),
        syscall!(timer_delete, INT),
        syscall!(clock_settime, INT, ADDR),
        syscall!(clock_gettime, INT, ADDR),
        syscall!(clock_getres, INT, ADDR),
        syscall!(clock_nanosleep, INT, INT, ADDR, ADDR),
        syscall!(syslog, INT, STR, INT),
        syscall!(ptrace, INT, INT, INT, INT),
        syscall!(sched_setparam, INT, ADDR),
        syscall!(sched_setscheduler, INT, INT, ADDR),
        syscall!(sched_getscheduler, INT),
        syscall!(sched_getparam, INT, ADDR),
        syscall!(sched_setaffinity, INT, INT, ADDR),
        syscall!(sched_getaffinity, INT, INT, ADDR),
        syscall!(sched_yield),
        syscall!(sched_get_priority_max, INT),
        syscall!(sched_get_priority_min, INT),
        syscall!(sched_rr_get_interval, INT, ADDR),
        syscall!(restart_syscall),
        syscall!(kill, INT, INT),
        syscall!(tkill, INT, INT),
        syscall!(tgkill, INT, INT, INT),
        syscall!(sigaltstack, ADDR, ADDR),
        syscall!(rt_sigsuspend, ADDR, INT),
        syscall!(rt_sigaction, INT, ADDR, ADDR, INT),
        syscall!(rt_sigprocmask, INT, ADDR, ADDR, INT),
        syscall!(rt_sigpending, ADDR, INT),
        syscall!(rt_sigtimedwait, ADDR, ADDR, ADDR, INT),
        syscall!(rt_sigqueueinfo, INT, INT, ADDR),
        syscall!(rt_sigreturn),
        syscall!(setpriority, INT, INT, INT),
        syscall!(getpriority, INT, INT),
        syscall!(reboot, INT, INT, INT, ADDR),
        syscall!(setregid, INT, INT),
        syscall!(setgid, INT),
        syscall!(setreuid, INT, INT),
        syscall!(setuid, INT),
        syscall!(setresuid, INT, INT, INT),
        syscall!(getresuid, ADDR, ADDR, ADDR),
        syscall!(setresgid, INT, INT, INT),
        syscall!(getresgid, ADDR, ADDR, ADDR),
        syscall!(setfsuid, INT),
        syscall!(setfsgid, INT),
        syscall!(times, ADDR),
        syscall!(setpgid, INT, INT),
        syscall!(getpgid, INT),
        syscall!(getsid, INT),
        syscall!(setsid),
        syscall!(getgroups, INT, ADDR),
        syscall!(setgroups, INT, ADDR),
        syscall!(uname, ADDR),
        syscall!(sethostname, STR, INT),
        syscall!(setdomainname, STR, INT),
        syscall!(getrlimit, INT, ADDR),
        syscall!(setrlimit, INT, ADDR),
        syscall!(getrusage, INT, ADDR),
        syscall!(umask, INT),
        syscall!(prctl, INT, INT, INT, INT, INT),
        syscall!(getcpu, ADDR, ADDR, ADDR),
        syscall!(gettimeofday, ADDR, ADDR),
        syscall!(settimeofday, ADDR, ADDR),
        syscall!(adjtimex, ADDR),
        syscall!(getpid),
        syscall!(getppid),
        syscall!(getuid),
        syscall!(geteuid),
        syscall!(getgid),
        syscall!(getegid),
        syscall!(gettid),
        syscall!(sysinfo, ADDR),
        syscall!(mq_open, STR, INT, INT, ADDR),
        syscall!(mq_unlink, STR),
        syscall!(mq_timedsend, INT, STR, INT, INT, ADDR),
        syscall!(mq_timedreceive, INT, STR, INT, ADDR, ADDR),
        syscall!(mq_notify, INT, ADDR),
        syscall!(mq_getsetattr, INT, ADDR, ADDR),
        syscall!(msgget, INT, INT),
        syscall!(msgctl, INT, INT, ADDR),
        syscall!(msgrcv, INT, ADDR, INT, INT, INT),
        syscall!(msgsnd, INT, ADDR, INT, INT),
        syscall!(semget, INT, INT, INT),
        syscall!(semctl, INT, INT, INT, INT),
        syscall!(semtimedop, INT, ADDR, INT, ADDR),
        syscall!(semop, INT, ADDR, INT),
        syscall!(shmget, INT, INT, INT),
        syscall!(shmctl, INT, INT, ADDR),
        syscall!(shmat, INT, STR, INT),
        syscall!(shmdt, STR),
        syscall!(socket, INT, INT, INT),
        syscall!(socketpair, INT, INT, INT, ADDR),
        syscall!(bind, INT, ADDR, INT),
        syscall!(listen, INT, INT),
        syscall!(accept, INT, ADDR, ADDR),
        syscall!(connect, INT, ADDR, INT),
        syscall!(getsockname, INT, ADDR, ADDR),
        syscall!(getpeername, INT, ADDR, ADDR),
        syscall!(sendto, INT, ADDR, INT, INT, ADDR, INT),
        syscall!(recvfrom, INT, ADDR, INT, INT, ADDR, ADDR),
        syscall!(setsockopt, INT, INT, INT, STR, INT),
        syscall!(getsockopt, INT, INT, INT, STR, ADDR),
        syscall!(shutdown, INT, INT),
        syscall!(sendmsg, INT, ADDR, INT),
        syscall!(recvmsg, INT, ADDR, INT),
        syscall!(readahead, INT, INT, INT),
        syscall!(brk, INT),
        syscall!(munmap, INT, INT),
        syscall!(mremap, INT, INT, INT, INT, INT),
        syscall!(add_key, STR, STR, ADDR, INT, INT),
        syscall!(request_key, STR, STR, STR, INT),
        syscall!(keyctl, INT, INT, INT, INT, INT),
        syscall!(clone, INT, INT, ADDR, INT, ADDR),
        syscall!(execve, STR, STRV, STRV),
        syscall!(mmap, ADDR, INT, INT, INT, INT, INT),
        syscall!(fadvise64, INT, INT, INT, INT),
        syscall!(swapon, STR, INT),
        syscall!(swapoff, STR),
        syscall!(mprotect, INT, INT, INT),
        syscall!(msync, INT, INT, INT),
        syscall!(mlock, INT, INT),
        syscall!(munlock, INT, INT),
        syscall!(mlockall, INT),
        syscall!(munlockall),
        syscall!(mincore, INT, INT, ADDR),
        syscall!(madvise, INT, INT, INT),
        syscall!(remap_file_pages, INT, INT, INT, INT, INT),
        syscall!(mbind, INT, INT, INT, ADDR, INT, INT),
        syscall!(get_mempolicy, ADDR, ADDR, INT, INT, INT),
        syscall!(set_mempolicy, INT, ADDR, INT),
        syscall!(migrate_pages, INT, INT, ADDR, ADDR),
        syscall!(move_pages, INT, INT, ADDR, ADDR, ADDR, INT),
        syscall!(rt_tgsigqueueinfo, INT, INT, INT, ADDR),
        syscall!(perf_event_open, ADDR, INT, INT, INT, INT),
        syscall!(accept4, INT, ADDR, ADDR, INT),
        syscall!(recvmmsg, INT, ADDR, INT, INT, ADDR),
    ],
    _260: [
        syscall!(wait4, INT, ADDR, INT, ADDR),
        syscall!(prlimit64, INT, INT, ADDR, ADDR),
        syscall!(fanotify_init, INT, INT),
        syscall!(fanotify_mark, INT, INT, INT, INT, STR),
        syscall!(name_to_handle_at, INT, STR, ADDR, INT, INT),
        syscall!(open_by_handle_at, INT, ADDR, INT),
        syscall!(clock_adjtime, INT, ADDR),
        syscall!(syncfs, INT),
        syscall!(setns, INT, INT),
        syscall!(sendmmsg, INT, ADDR, INT, INT),
        syscall!(process_vm_readv, INT, ADDR, INT, ADDR, INT, INT),
        syscall!(process_vm_writev, INT, ADDR, INT, ADDR, INT, INT),
        syscall!(kcmp, INT, INT, INT, INT, INT),
        syscall!(finit_module, INT, STR, INT),
        syscall!(sched_setattr, INT, ADDR, INT),
        syscall!(sched_getattr, INT, ADDR, INT, INT),
        syscall!(renameat2, INT, STR, INT, STR, INT),
        syscall!(seccomp, INT, INT, ADDR),
        syscall!(getrandom, STR, INT, INT),
        syscall!(memfd_create, STR, INT),
        syscall!(bpf, INT, ADDR, INT),
        syscall!(execveat, INT, STR, STRV, STRV, INT),
        syscall!(userfaultfd, INT),
        syscall!(membarrier, INT, INT, INT),
        syscall!(mlock2, INT, INT, INT),
        syscall!(copy_file_range, INT, ADDR, INT, ADDR, INT, INT),
        syscall!(preadv2, INT, ADDR, INT, INT, INT, INT),
        syscall!(pwritev2, INT, ADDR, INT, INT, INT, INT),
        syscall!(pkey_mprotect, INT, INT, INT, INT),
        syscall!(pkey_alloc, INT, INT),
        syscall!(pkey_free, INT),
        syscall!(statx, INT, STR, INT, INT, ADDR),
        syscall!(io_pgetevents, INT, INT, INT, ADDR, ADDR, ADDR),
        syscall!(rseq, ADDR, INT, INT, INT),
        syscall!(kexec_file_load, INT, INT, INT, STR, INT),
    ],
    _424: [
        syscall!(pidfd_send_signal, INT, INT, ADDR, INT),
        syscall!(io_uring_setup, INT, ADDR),
        syscall!(io_uring_enter, INT, INT, INT, INT, ADDR, INT),
        syscall!(io_uring_register, INT, INT, ADDR, INT),
        syscall!(open_tree, INT, STR, INT),
        syscall!(move_mount, INT, STR, INT, STR, INT),
        syscall!(fsopen, STR, INT),
        syscall!(fsconfig, INT, INT, STR, ADDR, INT),
        syscall!(fsmount, INT, INT, INT),
        syscall!(fspick, INT, STR, INT),
        syscall!(pidfd_open, INT, INT),
        syscall!(clone3, ADDR, INT),
        syscall!(close_range, INT, INT, INT),
        syscall!(openat2, INT, STR, ADDR, INT),
        syscall!(pidfd_getfd, INT, INT, INT),
        syscall!(faccessat2, INT, STR, INT, INT),
        syscall!(process_madvise, INT, ADDR, INT, INT, INT),
        syscall!(epoll_pwait2, INT, ADDR, INT, ADDR, ADDR, INT),
        syscall!(mount_setattr, INT, STR, INT, ADDR, INT),
        syscall!(quotactl_fd, INT, INT, INT, ADDR),
        syscall!(landlock_create_ruleset, ADDR, INT, INT),
        syscall!(landlock_add_rule, INT, ADDR, ADDR, INT),
        syscall!(landlock_restrict_self, INT, INT),
        syscall!(memfd_secret, INT),
        syscall!(process_mrelease, INT, INT),
        syscall!(futex_waitv, ADDR, INT, INT, ADDR, INT),
        syscall!(set_mempolicy_home_node, INT, INT, INT, INT),
        syscall!(cachestat, INT, INT, INT, INT),
        syscall!(fchmodat2, INT, STR, INT, INT),
    ],
};

pub fn get_arg_value(registers: user_regs_struct, i: usize) -> c_ulonglong {
    match i {
        0 => registers.regs[0],
        1 => registers.regs[1],
        2 => registers.regs[2],
        3 => registers.regs[3],
        4 => registers.regs[4],
        5 => registers.regs[5],
        v => panic!("Invalid system call index {v}!"),
    }
}

// test that all syscalls match their syscall number
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_syscall_numbers() {
        for (i, sysno, ..) in SYSCALLS._0.iter().enumerate() {
            if let Some((sysno, _)) = sysno {
                assert_eq!(i, sysno.id() as usize);
            }
        }
        for (i, sysno, ..) in SYSCALLS._260.iter().enumerate() {
            if let Some((sysno, _)) = sysno {
                assert_eq!(i + 260, sysno.id() as usize);
            }
        }
        for (i, sysno, ..) in SYSCALLS._424.iter().enumerate() {
            if let Some((sysno, _)) = sysno {
                assert_eq!(i + 424, sysno.id() as usize);
            }
        }
    }
}
