#include <terrain.h>
#include <terrain_tiles.h>
#include <camera.h>
#include <gfx.h>
#include <util.h>
#include <ctype.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct sprite terrain_sprites[MAX_TERRAIN_SPRITES];

struct tile
{
    const struct sprite *s;
    int start, end;
};

static int render_tile(const unsigned char id, const short x,
    const short y, const struct tile *const tiles, const size_t n)
{
    for (size_t i = 0; i < n; i++)
    {
        const struct tile *const rt = &tiles[i];

        if (id >= rt->start && id <= rt->end)
        {
            sprite_get_or_ret(s, -1);

            if (sprite_clone(rt->s, s))
            {
                fprintf(stderr, "%s: sprite_clone failed\n", __func__);
                return -1;
            }

            const unsigned char pos = id - rt->start;
            const short tx = pos % (s->w / TERRAIN_SZ),
                ty = (pos * TERRAIN_SZ) / s->w;

            s->x = x;
            s->y = y;
            s->w = TERRAIN_SZ;
            s->h = TERRAIN_SZ;
            s->u = tx * TERRAIN_SZ;
            s->v = ty * TERRAIN_SZ;

            const int ret = sprite_sort(s);

            if (ret)
                fprintf(stderr, "%s: sprite_sort failed\n", __func__);

            return ret;
        }
    }

    fprintf(stderr, "%s: unknown tile %#hhx\n", __func__, id);
    return -1;
}

static int render_ground(const struct terrain_tile *const t, const short x,
    const short y)
{
    static const struct tile tiles[] =
    {
#define TILE(t) {.s = &terrain_sprites[t], .start = t##_START, .end = t##_END}
        TILE(SIDEWALK),
        TILE(ROOF1),
        TILE(ROOF2),
        TILE(PAVEMENT),
        TILE(BUILDING1),
        TILE(BUILDING2)
#undef TILE
    };

    return render_tile(t->t, x, y, tiles, sizeof tiles / sizeof *tiles);
}

static int render_object(const struct terrain_tile *const t, const short x,
    const short y)
{
    if (t->obj == OBJECT_NONE)
        return 0;

    static const struct tile tiles[] =
    {
#define TILE(t) {.s = &terrain_sprites[t], .start = t##_START, .end = t##_END}
        TILE(GRASS),
#undef TILE
    };

    return render_tile(t->obj, x, y, tiles, sizeof tiles / sizeof *tiles);
}

int terrain_render(const struct terrain_map *const map,
    const struct camera *const cam)
{
    const int start_x = abs(cam->x / TERRAIN_SZ),
        start_y = abs(cam->y / TERRAIN_SZ);

    const int remx = cam->x % TERRAIN_SZ,
        remy = cam->y % TERRAIN_SZ;

    int nx = map->nx, ny = map->ny;

    if (abs(remx))
        nx++;

    if (abs(remy))
        ny++;

    struct m
    {
        size_t i;
        long p;
    };

    for (struct m x = {.i = start_x, .p = remx};
            x.i < nx + start_x; x.i++, x.p += TERRAIN_SZ)
        for (struct m y = {.i = start_y, .p = remy};
            y.i < ny + start_y; y.i++, y.p += TERRAIN_SZ)
        {
            const struct terrain_tile *const t = &map->m[y.i][x.i];

            if (render_ground(t, x.p, y.p)
                || render_object(t, x.p, y.p))
                return -1;
        }

    return 0;
}
