/*
 * wnix, a Unix-like operating system for WebAssembly applications.
 * Copyright (C) 2025  Xavier Del Campo Romero
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#ifndef FS_H
#define FS_H

#include <fs/inode.h>
#include <state.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdbool.h>
#include <stddef.h>

struct fs_mountpoint;
struct fs_mp_prv;
struct fs_fd_prv;

struct fs_mp
{
    const char *src, *tgt;
    const struct fs *fs;
    struct fs_mp_prv *prv;
};

struct fs_fd
{
    int error;
    off_t start, offset, size;
    struct fs_fd_prv *prv;
    struct fs_mp mp;
    const struct fs_mp *tgt_mp;
    union inode_result inode;
};

struct fs_stat
{
    const char *path;
    struct stat *sb;
    uid_t uid;
    gid_t gid;
};

struct fs_mkdir
{
    const char *path;
    mode_t mode;
    uid_t uid;
    gid_t gid;
};

struct fs_mount
{
    const char *src, *tgt;
    mode_t mode;
    uid_t uid;
    gid_t gid;
};

struct fs_umount
{
    const char *tgt;
    mode_t mode;
    uid_t uid;
    gid_t gid;
};

struct fs_stdstreams
{
    struct fs_fd streams[3];
};

struct fs_open
{
    const char *path;
    struct fs_fd *fd;
    int flags;
    mode_t mode;
    uid_t uid;
    gid_t gid;
    struct fs_stdstreams ss;
};

struct fs_read
{
    struct fs_fd *fd;
    void *buf;
    size_t n;
};

struct fs_write
{
    struct fs_fd *fd;
    const void *buf;
    size_t n;
};

struct fs_ret
{
    enum state (*f)(void *args);
    void *args;
};

enum {FS_DEV_REQUIRED = 1};

struct fs
{
    const char *type;
    int flags;
    int (*mount)(const struct fs_mount *, struct fs_ret *);
    int (*umount)(const struct fs_umount *, struct fs_ret *);
    int (*stat)(const struct fs_stat *, const struct fs_mp *,
        const union inode_result *, struct fs_ret *);
    int (*mkdir)(const struct fs_mkdir *, const struct fs_mp *,
        const union inode_result *, struct fs_ret *);
    int (*open)(const struct fs_open *, const struct fs_mp *,
        const union inode_result *, struct fs_ret *);
    int (*close)(struct fs_fd *);
    int (*read)(const struct fs_read *, struct fs_ret *);
    int (*read_nb)(const struct fs_read *);
    int (*write)(const struct fs_write *, struct fs_ret *);
    int (*eof)(const struct fs_fd *);
    struct inode_ops iops;
};

typedef int (*fs_update_fn)(struct fs_mp_prv *);

int fs_register(const struct fs *fs);
int fs_update(void);
int fs_mountpoint(const char *src, const char *tgt, const struct fs *fs,
    fs_update_fn fn, struct fs_mp_prv *pr);
const struct fs *fs_from_type(const char *type);
int fs_mp_from_path(const char *path, struct fs_mp *mp);
struct fs_mp *fs_mps_from_path(const char *path, size_t *n);
const char *fs_relpath(const char *path);
char *fs_parent(const char *path);
int fs_next(const char *path, char **next, size_t *i);

#endif
