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

Result<u64> sys_poll(Registers*, SyscallArgs args)
{
    struct pollfd* fds = (struct pollfd*)args[0];
    nfds_t nfds = (nfds_t)args[1];
    int timeout = (int)args[2];

    struct pollfd* kfds = (struct pollfd*)TRY(malloc_impl(nfds * sizeof(pollfd), false, false));
    auto guard = make_scope_guard([kfds] { free_impl(kfds); });

    if (!MemoryManager::copy_from_user(fds, kfds, nfds * sizeof(pollfd))) return err(EFAULT);

    auto* current = Scheduler::current();
    TRY(check_pledge(current, Promise::p_stdio));

    Vector<SharedPtr<VFS::Inode>> inodes;
    TRY(inodes.try_reserve(nfds));

    for (nfds_t i = 0; i < nfds; i++)
    {
        int fd = kfds[i].fd;
        auto maybe_inode = current->resolve_fd(fd);
        if (maybe_inode.has_error())
        {
            kwarnln("poll: fd %lu (%d) is not valid, storing POLLNVAL", i, fd);
            kfds[i].revents |= POLLNVAL;
            TRY(inodes.try_append({}));
        }
        else
            TRY(inodes.try_append(maybe_inode.release_value()->description->inode));
    }

    bool infinite = timeout < 0;
    nfds_t fds_with_events;

    do {
        fds_with_events = 0;
        for (nfds_t i = 0; i < nfds; i++)
        {
            auto& inode = inodes[i];
            if (!inode) continue;

            if (kfds[i].events & POLLIN && !inode->will_block_if_read())
            {
                fds_with_events++;
                kfds[i].revents |= POLLIN;
            }
        }

        if (!fds_with_events && (timeout > 0 || infinite))
        {
            kernel_sleep(10);
            timeout -= (10 - (int)current->sleep_ticks_left);
            if (current->interrupted)
            {
                guard.deactivate();
                free_impl(kfds);
                return err(EINTR);
            }
            continue;
        }

        break;
    } while (1);

    MemoryManager::copy_to_user(fds, kfds, nfds * sizeof(pollfd));

    return fds_with_events;
}