#include "Log.h"
#include "Pledge.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
#include <bits/signal.h>

Result<u64> sys_sigreturn(Registers* regs, SyscallArgs)
{
    auto* current = Scheduler::current();

    current->sigreturn(regs);

    // Since we could be returning to anywhere in the program, we don't want to be changing the return value register
    // (RAX on x86_64). But our syscall framework doesn't allow that, so we just set it to its current value.
    return current->return_register();
}

Result<u64> sys_sigaction(Registers*, SyscallArgs args)
{
    auto* current = Scheduler::current();
    TRY(check_pledge(current, Promise::p_signal));

    int signo = (int)args[0];
    const struct sigaction* act = (const struct sigaction*)args[1];
    struct sigaction* oldact = (struct sigaction*)args[2];
    void* sigreturn = (void*)args[3];

    if (signo > NSIG) return err(EINVAL);
    if (signo <= 0) return err(EINVAL);

    if (oldact)
    {
        if (!MemoryManager::copy_to_user_typed(oldact, &current->signal_handlers[signo - 1])) return err(EFAULT);
    }

    if (act)
    {
        struct sigaction kact;
        if (!MemoryManager::copy_from_user_typed(act, &kact)) return err(EFAULT);
        kact.__sa_sigreturn = sigreturn;

        current->signal_handlers[signo - 1] = kact;
    }

    return 0;
}

Result<u64> sys_kill(Registers*, SyscallArgs args)
{
    auto* current = Scheduler::current();
    TRY(check_pledge(current, Promise::p_proc));

    pid_t pid = (pid_t)args[0];
    int signo = (int)args[1];

    // FIXME: Support this case.
    if (pid <= 0) return err(ENOTSUP);

    auto* target = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
    if (current->auth.euid != 0 && current->auth.euid != target->auth.euid && current->auth.egid != target->auth.egid)
        return err(EPERM);
    if (target->is_kernel) return 0;
    if (signo == 0) return 0;

    target->send_signal(signo);

    return 0;
}

Result<u64> sys_sigprocmask(Registers*, SyscallArgs args)
{
    auto* current = Scheduler::current();
    TRY(check_pledge(current, Promise::p_signal));

    int how = (int)args[0];
    const sigset_t* set = (const sigset_t*)args[1];
    sigset_t* oldset = (sigset_t*)args[2];

    if (oldset)
    {
        if (!MemoryManager::copy_to_user_typed(oldset, &current->signal_mask)) return err(EFAULT);
    }

    if (set)
    {
        sigset_t kset;
        if (!MemoryManager::copy_from_user_typed(set, &kset)) return err(EFAULT);

        switch (how)
        {
        case SIG_BLOCK: current->signal_mask |= kset; break;
        case SIG_UNBLOCK: current->signal_mask &= ~kset; break;
        case SIG_SETMASK: current->signal_mask = kset; break;
        default: return err(EINVAL);
        }
    }

    return 0;
}