#include "thread/Thread.h"
#include "memory/MemoryManager.h"
#include <bits/atfile.h>
#include <bits/open-flags.h>
#include <luna/Alloc.h>
#include <luna/Atomic.h>
#include <luna/PathParser.h>

static Atomic<u64> g_next_id;

LinkedList<Thread> g_threads;

void Thread::init()
{
    g_next_id = 2;
}

Result<Thread*> new_thread()
{
    Thread* const thread = TRY(make<Thread>());

    thread->id = g_next_id++;

    return thread;
}

Result<int> Thread::allocate_fd(int min)
{
    if (min < 0 || min >= FD_MAX) return err(EINVAL);
    for (int i = min; i < FD_MAX; i++)
    {
        // FIXME: Possible race condition if multiple threads share a FileDescriptorTable? Let's not worry about it for
        // now, we're still a long way away from reaching that point.
        if (!fd_table[i].has_value()) { return i; }
    }

    return err(EMFILE);
}

Result<FileDescriptor*> Thread::resolve_fd(int fd)
{
    if (fd < 0 || fd >= FD_MAX) return err(EBADF);

    Option<FileDescriptor>& maybe_descriptor = fd_table[fd];

    if (!maybe_descriptor.has_value()) return err(EBADF);

    return maybe_descriptor.value_ptr();
}

Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
                                                     bool follow_last_symlink, SharedPtr<VFS::Inode>* parent_inode)
{
    if (parent_inode) *parent_inode = this->current_directory;

    if (PathParser::is_absolute(path.view()))
        return VFS::resolve_path(path.chars(), this->auth, {}, follow_last_symlink);

    if (dirfd == AT_FDCWD)
        return VFS::resolve_path(path.chars(), this->auth, this->current_directory, follow_last_symlink);

    auto descriptor = TRY(resolve_fd(dirfd));

    if (parent_inode) *parent_inode = descriptor->inode;

    if (path.is_empty() && allow_empty_path) return descriptor->inode;

    return VFS::resolve_path(path.chars(), this->auth, descriptor->inode, follow_last_symlink);
}

bool FileDescriptor::should_append()
{
    return flags & O_APPEND;
}

bool FileDescriptor::should_block()
{
    return !(flags & O_NONBLOCK);
}

bool FileDescriptor::is_readable()
{
    return flags & O_RDONLY;
}

bool FileDescriptor::is_writable()
{
    return flags & O_WRONLY;
}