/* Copyright (C) Eugene 'Vindex' Stulin, 2023-2026
 *
 * 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 with 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 <mountpoint> [options]\n"(progname);
    writefln(
`File-system specific options:
    --name=<s>          Name of the "hello" file
                        (default: "hello")
    --contents=<s>      Contents of the "hello" file
                        (default "Hello, World!\\n")
Example: %s /tmp/hellopoint --contents="I am a file system"
`,
    progname
    );
}


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

    override void initialize(ConnInfo conn, FuseConfig cfg) {
        cfg.getRawPointer().kernel_cache = 1;
    }

    override stat_t getattr(string path, FileInfo 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, FileInfo fi) {
        fe(path == "/", ENOENT);
        return [".", "..", this.filename];
    }

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

    override int read(string p, ubyte[] buf, off_t offset, FileInfo fi) {
        fe(p[1 .. $] == this.filename, ENOENT);
        size_t size = buf.length;
        if (offset < contents.length) {
            if (offset + size > contents.length) {
                size = contents.length - offset;
            }
            memcpy(buf.ptr, this.contents.ptr + offset, 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 || args.length == 1) {
        showHelp(args[0]);
        showFuseHelp();
        return 0;
    }
    string mountpoint = args[1];
    string[] fuseArgs;
    if (args.length > 2) {
        fuseArgs = args[2 .. $];
    }
    auto fs = new Hello(filename, contents);
    auto ret = oxfuse.mount("Hello", fs, mountpoint, fuseArgs);
    if (0 != ret) {
        stderr.writefln!"FUSE has been exited with code %s"(ret);
    }
    return ret;
}

