/*
 * 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 <bin.h>
#include <bin/types.h>
#include <bin/wnix/routines.h>
#include <endian.h>
#include <nanowasm/nw.h>
#include <stdlib.h>

struct set_errno
{
    struct endian_le32 error;
    struct nw_sm_io io;
    struct bin_set_errno cfg;
    struct nw_next next;
};

static enum nw_state store_errno(void *const user, struct nw_next *const next)
{
    struct set_errno *const e = user;
    const struct bin_set_errno *const cfg = &e->cfg;
    struct bin_proc *const p = cfg->p;
    const enum nw_state n = nw_linear_store(&p->instance, &e->io, cfg->addr);

    switch (n)
    {
        case NW_FATAL:
            free(e);
            /* Fall through.*/
        case NW_AGAIN:
            return n;

        case NW_OK:
            *next = e->next;
            free(e);
            break;
    }

    return NW_AGAIN;
}

int bin_wnix_set_errno(const struct bin_set_errno *const cfg,
    struct nw_next *const next)
{
    struct set_errno *const e = malloc(sizeof *e);

    if (!e)
        return -1;

    *e = (const struct set_errno)
    {
        .error = endian_to_le32(cfg->error),
        .cfg = *cfg,
        .next = *next,
        .io =
        {
            .buf = &e->error,
            .n = sizeof e->error
        }
    };

    *next = (const struct nw_next){.fn = store_errno, .user = e};
    return 0;
}
