#pragma once
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>

typedef long ssize_t;

#define VFS_FILE 0x0
#define VFS_DIRECTORY 0x1
#define VFS_DEVICE 0x2

#define VFS_MOUNTPOINT 0x1

#define NAME_MAX 64

namespace VFS
{
    struct Node;

    typedef ssize_t (*node_read)(Node*, size_t, size_t, char*);
    typedef ssize_t (*node_write)(Node*, size_t, size_t, const char*);
    typedef Node* (*node_finddir)(Node*, const char*);
    typedef int (*node_mkdir)(Node*, const char*, mode_t);
    typedef int (*node_block)(Node*);
    typedef Node* (*node_readdir)(Node*, long);
    typedef uintptr_t (*node_mmap)(Node*, uintptr_t, size_t, int, off_t);
    typedef long (*node_ioctl)(Node*, int, uintptr_t);

    struct Node
    {
        char name[NAME_MAX];
        int type;
        int flags;
        int tty = 0;
        int uid;
        int gid;
        mode_t mode;
        uint64_t impl;
        uint64_t atime;
        uint64_t ctime;
        uint64_t mtime;
        uint64_t inode;
        uint64_t length;
        node_read read_func;
        node_finddir find_func;
        node_readdir readdir_func;
        node_mkdir mkdir_func;
        node_write write_func;
        node_block block_func;
        node_mmap mmap_func;
        node_ioctl ioctl_func;
        Node* link;
    };

    ssize_t read(Node* node, size_t offset, size_t length, char* buffer);
    ssize_t write(Node* node, size_t offset, size_t length, const char* buffer);
    int mkdir(const char* path, const char* name);
    int mkdir(const char* pathname);

    int do_mkdir(const char* path, const char* name, int uid, int gid, mode_t mode);
    int do_mkdir(const char* pathname, int uid, int gid, mode_t mode);

    int would_block(Node* node);

    void mount_root(Node* root);

    Node* resolve_path(const char* filename, Node* root = nullptr);

    bool exists(const char* pathname);

    void mount(Node* mountpoint, Node* mounted);
    void mount(const char* pathname, Node* mounted);

    void unmount(Node* mountpoint);

    Node* root();

    Node* readdir(Node* dir, long offset);

    bool can_execute(Node* node, int uid, int gid);
    bool can_read(Node* node, int uid, int gid);
    bool can_write(Node* node, int uid, int gid);

    bool is_setuid(Node* node);
    bool is_setgid(Node* node);
}