#include "fs/VFS.h"
#include "interrupts/Context.h"
#include "std/errno.h"
#include "std/stdlib.h"
#include "sys/UserMemory.h"
#include "thread/Scheduler.h"

typedef unsigned long off_t;
typedef unsigned short mode_t;
typedef unsigned long ino_t;

struct stat // FIXME: This struct is quite stubbed out.
{
    ino_t st_ino;
    mode_t st_mode;
    off_t st_size;
};

void do_stat(Context* context, VFS::Node* node, struct stat* buf)
{
    struct stat* kstat = obtain_user_ref(buf);
    if (!kstat)
    {
        context->rax = -EFAULT; // FIXME: The manual doesn't say fstat can return EFAULT, but it seems logical here...
        return;
    }
    kstat->st_ino = node->inode;
    kstat->st_mode = (mode_t)node->type;
    kstat->st_size = node->length;
    release_user_ref(kstat);
    context->rax = 0;
}

void sys_fstat(Context* context, int fd, struct stat* buf)
{
    Task* current_task = Scheduler::current_task();
    if (fd < 0 || fd >= TASK_MAX_FDS)
    {
        context->rax = -EBADF;
        return;
    }
    Descriptor& file = current_task->files[fd];
    if (!file.is_open())
    {
        context->rax = -EBADF;
        return;
    }
    VFS::Node* node = file.node();
    return do_stat(context, node, buf);
}

void sys_stat(Context* context, const char* path, struct stat* buf)
{
    char* kpath = strdup_from_user(path);
    if (!kpath)
    {
        context->rax = -EFAULT;
        return;
    }
    VFS::Node* node = VFS::resolve_path(kpath);
    kfree(kpath);
    if (!node)
    {
        context->rax = -ENOENT;
        return;
    }
    return do_stat(context, node, buf);
}