/*
 * 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 <fs/inode.h>
#include <fs/fs.h>
#include <fs/types.h>
#include <state.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>

struct search
{
    struct fs_mp *mp;
    size_t n, mp_i;
    struct fs_ret *r;
    union inode_result inode;
    struct inode_search search;
};

static void free_search(struct search *const s)
{
    if (!s)
        return;

    free(s->mp);
    free(s);
}

static int search_mp(struct search *const s)
{
    const struct inode_search *const is = &s->search;
    const struct fs_mp *const cur = &s->mp[s->mp_i],
        *const next = s->mp_i + 1 < s->n ?
            &s->mp[s->mp_i + 1] : NULL;
    const struct fs *const fs = cur->fs;
    const char *relpath = next ? next->tgt : is->path;

    for (size_t i = 0; i < s->mp_i; i++)
        relpath += strlen(s->mp[s->mp_i].tgt);

    if (fs && fs->iops.search(relpath, cur, &s->inode, s->r))
        return -1;

    s->mp_i++;
    return 0;
}

static enum state search(void *const args)
{
    enum state ret = STATE_FATAL;
    struct search *const s = args;
    const struct inode_search *const is = &s->search;

    if (!s->mp)
    {
        if (!is->done(STATE_FATAL, NULL, NULL, NULL, is->args))
            ret = STATE_AGAIN;
    }
    else if (s->mp_i < s->n)
    {
        if (!search_mp(s))
            return STATE_AGAIN;
    }
    else
    {
        const char *relpath = is->path;
        const struct fs_mp mp = s->mp[s->n - 1];

        for (size_t i = 0; i < s->n; i++)
            relpath += strlen(s->mp[i].tgt);

        if (!is->done(STATE_OK, relpath, &mp, &s->inode, is->args))
            ret = STATE_AGAIN;
    }

    free_search(s);
    return ret;
}

int inode_search(const struct inode_search *const is, struct fs_ret *const r)
{
    size_t n;
    struct search *s = NULL;
    struct fs_mp *const mp = fs_mps_from_path(is->path, &n);

    if (!(s = malloc(sizeof *s)))
        goto failure;

    *s = (const struct search)
    {
        .search = *is,
        .mp = mp,
        .n = n,
        .r = r
    };

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

    return 0;

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