// Host-side test for examples/agi/agiView.c.
//
// Loads a single VIEW resource, parses it, dumps the loop/cel
// structure, and writes cel 0 of every loop as a PPM image. The
// transparent color is rendered as bright magenta so the cel's
// silhouette stands out from the actual painted pixels.
//
// Usage:
//   testAgiView <game-directory> [view-id]
//
// Exit codes:
//   0  parsed; outputs written
//   1  game directory missing, view slot empty, or parse failed
//   2  invalid command line

#include "agi.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define CHANNEL_SCALE       17u

// Pseudo-color used for "transparent" pixels in the output image.
#define TRANS_R             0xFFu
#define TRANS_G             0x00u
#define TRANS_B             0xFFu


// ----- Prototypes -----

static int16_t  firstNonEmptyView(const AgiGameT *game);
static bool     writeCelPpm(const char *path, const AgiCelInfoT *cel);


// ----- Helpers (alphabetical) -----

static int16_t firstNonEmptyView(const AgiGameT *game) {
    uint16_t  count;
    uint16_t  i;

    count = game->resCount[AGI_RES_VIEW];
    for (i = 0u; i < count; i++) {
        if (game->resDir[AGI_RES_VIEW][i].volume < AGI_MAX_VOLUMES) {
            return (int16_t)i;
        }
    }
    return -1;
}


// Walk the cel's RLE data, paint into a chunky 8bpp buffer, then
// emit a PPM with transparent pixels mapped to magenta. Same RLE
// rules as agiViewDraw: each byte = (color << 4) | runLen, runLen == 0
// ends the row.
static bool writeCelPpm(const char *path, const AgiCelInfoT *cel) {
    FILE           *fp;
    uint8_t        *buf;
    uint16_t        w;
    uint16_t        h;
    uint16_t        cx;
    uint16_t        cy;
    uint16_t        i;
    const uint8_t  *rle;
    uint8_t         rleByte;
    uint8_t         color;
    uint8_t         runLen;
    uint8_t         r;
    uint8_t         transparent;
    uint16_t        rgb;

    w = (uint16_t)cel->width;
    h = (uint16_t)cel->height;
    if (w == 0u || h == 0u) {
        return false;
    }

    buf = (uint8_t *)malloc((size_t)w * (size_t)h);
    if (buf == NULL) {
        return false;
    }
    transparent = cel->transparentColor;
    memset(buf, transparent, (size_t)w * (size_t)h);

    rle = cel->rleData;
    for (cy = 0u; cy < h; cy++) {
        cx = 0u;
        for (;;) {
            rleByte = *rle++;
            if (rleByte == 0u) {
                break;
            }
            color  = (uint8_t)(rleByte >> 4);
            runLen = (uint8_t)(rleByte & 0x0Fu);
            for (r = 0u; r < runLen; r++) {
                if (cx >= w) {
                    break;
                }
                buf[cy * w + cx] = color;
                cx++;
            }
        }
    }

    fp = fopen(path, "wb");
    if (fp == NULL) {
        free(buf);
        return false;
    }
    fprintf(fp, "P6\n%u %u\n255\n", (unsigned)w, (unsigned)h);
    for (i = 0u; i < (uint16_t)(w * h); i++) {
        if (buf[i] == transparent) {
            fputc(TRANS_R, fp);
            fputc(TRANS_G, fp);
            fputc(TRANS_B, fp);
        } else {
            rgb = kAgiPalette[buf[i] & 0x0Fu];
            fputc((int)(((rgb >> 8) & 0x0Fu) * CHANNEL_SCALE), fp);
            fputc((int)(((rgb >> 4) & 0x0Fu) * CHANNEL_SCALE), fp);
            fputc((int)((rgb        & 0x0Fu) * CHANNEL_SCALE), fp);
        }
    }
    fclose(fp);
    free(buf);
    return true;
}


// ----- main -----

int main(int argc, char **argv) {
    AgiGameT   game;
    AgiViewT   view;
    int16_t    viewId;
    uint8_t   *bytes;
    uint16_t   length;
    uint8_t    li;
    uint8_t    ci;
    const AgiCelInfoT  *cel;
    char       path[256];

    if (argc < 2 || argc > 3) {
        fprintf(stderr, "usage: %s <game-directory> [view-id]\n", argv[0]);
        return 2;
    }

    if (!agiResOpen(&game)) {
        fprintf(stderr, "FAIL: agiResOpen rejected '%s'\n", argv[1]);
        return 1;
    }

    if (argc == 3) {
        viewId = (int16_t)atoi(argv[2]);
    } else {
        viewId = firstNonEmptyView(&game);
    }
    if (viewId < 0 || (uint16_t)viewId >= game.resCount[AGI_RES_VIEW]) {
        fprintf(stderr, "FAIL: no VIEW at id %d\n", (int)viewId);
        agiResClose(&game);
        return 1;
    }

    bytes = agiResLoad(&game, AGI_RES_VIEW, (uint16_t)viewId, &length);
    if (bytes == NULL) {
        fprintf(stderr, "FAIL: VIEW %d failed to load\n", (int)viewId);
        agiResClose(&game);
        return 1;
    }

    if (!agiViewParse(&view, bytes, length)) {
        fprintf(stderr, "FAIL: VIEW %d parse rejected\n", (int)viewId);
        agiResClose(&game);
        return 1;
    }

    printf("VIEW %d: %u bytes, %u loops\n", (int)viewId, (unsigned)length, (unsigned)view.loopCount);
    for (li = 0u; li < view.loopCount; li++) {
        printf("  loop %u: %u cels\n", (unsigned)li, (unsigned)view.loops[li].celCount);
        for (ci = 0u; ci < view.loops[li].celCount; ci++) {
            cel = &view.loops[li].cels[ci];
            printf("    cel %u: %ux%u  trans=%u  mirror=%u (src loop %u)\n",
                   (unsigned)ci, (unsigned)cel->width, (unsigned)cel->height,
                   (unsigned)cel->transparentColor,
                   (unsigned)cel->mirrored, (unsigned)cel->mirrorSourceLoop);
        }
        if (view.loops[li].celCount > 0u) {
            snprintf(path, sizeof(path), "build/tests/view%d_loop%u_cel0.ppm", (int)viewId, (unsigned)li);
            if (!writeCelPpm(path, &view.loops[li].cels[0])) {
                fprintf(stderr, "  warn: could not write %s\n", path);
            }
        }
    }

    agiViewFree(&view);
    agiResClose(&game);
    return 0;
}
