/*
 * 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/>.
 */

#include <ramfs.h>
#include <ramfs/types.h>
#include <fs/fs.h>
#include <state.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

struct search
{
    union inode_result *inode;
    const struct ramfs *rfs;
    struct fs_ret r, *pr;
    const char *path;
};

static void free_search(struct search *const s)
{
    free(s);
}

static int find_node(const char *const path, struct inode **const out,
    size_t *const i)
{
    int ret = -1;
    char *node;
    struct inode *volatile const inode = *out;

    if (fs_next(path, &node, i))
        return -1;
    else if (!strcmp(inode->name, node))
    {
        if (*i >= strlen(path))
        {
            free(node);
            return 1;
        }

        *out = inode->child;
    }
    else
    {
        bool found = false;

        for (struct inode *in = inode->right; in; in = in->right)
            if (!strcmp(in->name, node))
            {
                if (*i >= strlen(path))
                {
                    *out = in;
                    free(node);
                    return 1;
                }

                *out = in->child;
                found = true;
                break;
            }

        if (!found)
        {
            errno = ENOENT;
            goto end;
        }
    }

    ret = 0;

end:
    free(node);
    return ret;
}

static enum state search(void *const args)
{
    enum state ret = STATE_FATAL;
    struct search *const s = args;
    struct inode *inode = s->rfs->root;
    size_t i = 0;

    for (;;)
    {
        const int n = find_node(s->path, &inode, &i);

        if (n < 0)
            goto end;
        else if (n > 0)
            break;
    }

    s->inode->memi = inode;
    *s->pr = s->r;
    ret = STATE_AGAIN;

end:
    free_search(s);
    return ret;
}

int ramfs_search(const char *const path, const struct ramfs *const rfs,
    union inode_result *const inode, struct fs_ret *const r)
{
    struct search *const s = malloc(sizeof *s);

    if (!s)
        goto failure;

    *s = (const struct search)
    {
        .inode = inode,
        .path = path,
        .rfs = rfs,
        .r = *r,
        .pr = r
    };

    *r = (const struct fs_ret)
    {
        .f = search,
        .args = s
    };

    return 0;

failure:
    free(s);
    return -1;
}
