/*
 * 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 <drv/event.h>
#include <drv/tty.h>
#include <drv/tty/font.h>
#include <drv/tty/ops.h>
#include <drv/tty/types.h>
#include <gfx/gfx.h>
#include <gfx/sprite.h>
#include <stdbool.h>

static int init(struct drv_port *const p)
{
    if (p->init)
        return 0;

    const struct drv_event *const ev = &p->ev;
    const struct drv_event_ops ops =
    {
        .write = drv_tty_write,
        .p = p
    };

    if (ev->status("tty", &ops, true, 0666, ev->args))
        return -1;

    p->init = true;
    return 0;
}

static short get_y(const struct drv_port *const p)
{
    const ptrdiff_t r = p->proc_row - p->first_row;

    if (r >= 0)
        return r * 16;

    return (((p->rows + p->nrows) - p->first_row)
        + (p->proc_row - p->rows)) * 16;
}

static int prepare(struct drv_port *const p)
{
    struct row *r = p->proc_row;

    if (r == p->last_row)
        return 0;

    struct gfx_rect *const rect = gfx_rect_get();
    const short y = get_y(p);
    short x = 0;

    if (!rect)
        return 0;

    gfx_rect_init(rect);
    rect->y = y;
    rect->w = screen_w;
    rect->h = 16;

    if (gfx_rect_sort(rect))
        return -1;

    /* TODO: this is a trick to avoid having half-rendered lines. */
    struct gfx_sprite *sprites[64];

    for (size_t i = 0; i < sizeof sprites / sizeof *sprites; i++)
        sprites[i] = gfx_sprite_get();

    for (const char *c = r->columns;
        *c && c - r->columns < p->ncolumns; c++, x += 10)
    {
        struct gfx_sprite *const s = sprites[c - r->columns];

        if (*c == ' ')
            continue;

        const char ch = *c - ' ';
        const short u = (12 * ch) % ttyfont_spr.w;
        const short v = 16 * ((12 * ch) / ttyfont_spr.w);

        if (!s || gfx_sprite_clone(&ttyfont_spr, s))
            return 0;

        /* TODO: replace hardcoded values */
        s->x = x;
        s->y = y;
        s->w = 12;
        s->h = 16;
        s->u += u;
        s->v += v;

        if (gfx_sprite_sort(s))
            return -1;
    }

    if (++r - p->rows >= p->nrows)
        r = p->rows;

    p->proc_row = r;
    return 0;
}

int drv_tty_update(struct drv_port *const p)
{
    if (init(p) || prepare(p))
        return -1;

    return 0;
}
