#include <vfs.h>
#include <process.h>
#include <scheduler.h>

#include <lib/memory.h>
#include <lib/string.h>
#include <lib/print.h>

vfs_node_t* root = NULL;

vfs_node_t* vfs_create_root() {
    root = kalloc(sizeof(vfs_node_t));
    memset((uint8_t*)root, 0, sizeof(vfs_node_t));

    strcpy(root->name, "root");

    root->flags |= VFS_DIRECTORY;
    root->size = 0;
    root->find = NULL;

    return root;
}

void vfs_mount(vfs_node_t* _mnt, vfs_node_t* _fs) {
    if (!_fs || !_mnt || !(_mnt->flags & VFS_DIRECTORY)) return;
    _mnt->mnt = _fs;
}

vfs_node_t* vfs_find(const char* path) {
    vfs_node_t* current = root;
    if (current->mnt) {
        current = current->mnt;
    }

    char* sav = NULL;
    size_t path_sz = strlen(path) + 1;
    char* path_cpy = kalloc(path_sz);
    memset((uint8_t*)path_cpy, 0, path_sz);
    memcpy(path, path_cpy, path_sz);

    char* tkn = strtok_r(path_cpy, "/", &sav);
    while (tkn != NULL) {
        if (current && current->mnt) {
            current = current->mnt;
        }

        if (!current || !current->find) {
            return NULL;
        }
        
        current = current->find(current, tkn);

        if (current == NULL) {
            return NULL;
        }

        tkn = strtok_r(NULL, "/", &sav);
        if (tkn && !(current->flags & VFS_DIRECTORY)) {
            return NULL;
        }
    }

    kfree(path_cpy);
    return current;
}

vfs_node_t* vfs_mkdir(vfs_node_t* parent, const char* name) {
    if (!parent || !name) {
        return NULL;
    }

    if (strlen(name) == 0) {
        return NULL;
    }

    return parent->mkdir(parent, name);
}

int vfs_open(const char* _path, int flags) {
    vfs_node_t* file = vfs_find(_path);
    if (!file) return -1;

    file_t* f = (file_t*)kalloc(sizeof(file_t));
    f->node = file;
    f->offset = 0;
    f->flags = flags;

    return alloc_fd(f);
}

int vfs_read(int fd, void* buf, size_t sz) {
    process_t* curr_p = current_task();
    if (fd < 0 || fd >= MAX_PROCESS_FDS || !curr_p->fd_table[fd]->node) {
        return -1;
    }

    file_t* file = curr_p->fd_table[fd];

    if (!file->node->read) return -1;

    uint32_t bytes_read = file->node->read(file->node, file->offset, sz, buf);
    file->offset += bytes_read;

    return bytes_read;
}

int vfs_seek(int fd, size_t offset) {
    process_t* curr_p = current_task();
    if (fd < 0 || fd >= MAX_PROCESS_FDS || !curr_p->fd_table[fd]->node) {
        return 0;
    }

    file_t* file = curr_p->fd_table[fd];
    file->offset = offset;
    return 1;
}  

int alloc_fd(file_t* f) {
    process_t* curr_p = current_task();
    if (!curr_p) return -1;

    for (int i = 0; i < MAX_PROCESS_FDS; i++) {
        if (curr_p->fd_table[i] == NULL) {
            curr_p->fd_table[i] = f;
            return i;
        }
    }

    return -1;
}