/* Copyright (C) Eugene 'Vindex' Stulin, 2023-2024
 *
 * This example is written according to the original from libfuse.
 * Distributed under
 * the Boost Software License 1.0 or (at your option)
 * the GNU General Public License 3.0 or later.
 * This file is offered as-is, without any warranty.
 */

/**
 * Simple file system containing a single file.
 * The file name and its content are determined by command line arguments.
 */

import std.stdio;
import std.conv : o = octal;
import std.getopt;

import core.sys.posix.sys.stat : stat_t, S_IFDIR, S_IFREG;
import core.stdc.errno : ENOENT, EACCES;
import core.sys.posix.fcntl : O_ACCMODE, O_RDONLY;
import core.stdc.string : memcpy;

import oxfuse;


void showHelp(string progname) {
    writefln!"usage: %s [options] <mountpoint>\n"(progname);
    writefln!"%s\n%s\n%s\n%s\n%s"(
        "File-system specific options:",
        "    --name=<s>          Name of the 'hello' file",
        "                        (default: 'hello')",
        "    --contents=<s>      Contents 'hello' file",
        "                        (default 'Hello, World!\\n')"
    );
}


class Hello : FileSystem {
    this(string filename, string contents) {
        this.filename = filename;
        this.contents = contents;
    }

    override void initialize(fuse_conn_info* conn, fuse_config* cfg) {
        cfg.kernel_cache = 1;
    }

    override stat_t getattr(string path, ffi_t* fi) {
        stat_t st;
        if (path == "/") {
            st.st_mode = S_IFDIR | o!755;
            st.st_nlink = 2;
        } else if (this.filename == path[1 .. $]) {
            st.st_mode = S_IFREG | o!444;
            st.st_nlink = 1;
            st.st_size = this.contents.length;
        } else {
            fe(false, ENOENT);
        }
        return st;
    }

    override string[] readdir(string path, ffi_t* fi) {
        fe(path == "/", ENOENT);
        return [".", "..", this.filename];
    }

    override void open(string path, ffi_t* fi) {
        fe(path[1 .. $] == this.filename, ENOENT);
        fe((fi.flags & O_ACCMODE) == O_RDONLY, EACCES);
    }


    override int read(string p, ubyte* buf, size_t size, off_t off, ffi_t* fi) {
        fe(p[1 .. $] == this.filename, ENOENT);
        if (off < contents.length) {
            if (off + size > contents.length) {
                size = contents.length - off;
            }
            memcpy(buf, this.contents.ptr + off, size);
        } else {
            size = 0;
        }
        return cast(int)size;
    }

    private string filename, contents;
}


int main(string[] args) {
    string filename = "hello";
    string contents = "Hello, World!\n";
    auto getoptResult = args.getopt(
        config.passThrough,
        "name", &filename,
        "contents", &contents
    );
    if (getoptResult.helpWanted) {
        showHelp(args[0]);
        showFuseHelp();
        return 0;
    }
    string mountpoint = args[$-1];
    args = args[1 .. $-1] ~ [];
    auto fs = new Hello(filename, contents);
    fs.setMountPoint(mountpoint);
    auto ret = oxfuse.mount("Hello", fs, args);
    if (0 != ret) {
        stderr.writefln!"FUSE has been exited with code %s"(ret);
    }
    return ret;
}

