// Host-side test for examples/agi/agiPic.c.
//
// Loads a single PIC resource from a game directory, decodes it, and
// writes two image files: a color PPM for the visual plane (palette
// expanded to 8-bit RGB) and a grayscale PGM for the priority plane.
// Also reports the byte count and pixel coverage so a regression in
// the decoder shows up as a numeric diff.
//
// Usage:
//   testAgiPic <game-directory> [pic-id]
//
// The default pic-id is the first non-empty entry in PICDIR; KQ3 has
// id 0 unused so the loader hops forward until it finds one.
//
// Exit codes:
//   0  decoded; outputs written
//   1  game directory missing, PIC slot empty, or decode failed
//   2  invalid command line

#include "agi.h"

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


// 4-bit channel -> 8-bit channel scaling: x * 17 maps 0..15 to 0..255
// without bias (15 * 17 = 255 exactly).
#define CHANNEL_SCALE       17


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

static int16_t  firstNonEmptyPic(const AgiGameT *game);
static bool     writePgm(const char *path, const uint8_t *plane);
static bool     writePpm(const char *path, const uint8_t *plane);


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

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

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


static bool writePgm(const char *path, const uint8_t *plane) {
    FILE     *fp;
    uint32_t  i;
    uint8_t   gray;

    fp = fopen(path, "wb");
    if (fp == NULL) {
        return false;
    }
    fprintf(fp, "P5\n%d %d\n255\n", AGI_PIC_WIDTH, AGI_PIC_HEIGHT);
    for (i = 0u; i < (uint32_t)AGI_PIC_PIXELS; i++) {
        gray = (uint8_t)((plane[i] & 0x0Fu) * CHANNEL_SCALE);
        fputc(gray, fp);
    }
    fclose(fp);
    return true;
}


static bool writePpm(const char *path, const uint8_t *plane) {
    FILE     *fp;
    uint32_t  i;
    uint16_t  rgb;
    uint8_t   r;
    uint8_t   g;
    uint8_t   b;

    fp = fopen(path, "wb");
    if (fp == NULL) {
        return false;
    }
    fprintf(fp, "P6\n%d %d\n255\n", AGI_PIC_WIDTH, AGI_PIC_HEIGHT);
    for (i = 0u; i < (uint32_t)AGI_PIC_PIXELS; i++) {
        rgb = kAgiPalette[plane[i] & 0x0Fu];
        r   = (uint8_t)(((rgb >> 8) & 0x0Fu) * CHANNEL_SCALE);
        g   = (uint8_t)(((rgb >> 4) & 0x0Fu) * CHANNEL_SCALE);
        b   = (uint8_t)((rgb        & 0x0Fu) * CHANNEL_SCALE);
        fputc(r, fp);
        fputc(g, fp);
        fputc(b, fp);
    }
    fclose(fp);
    return true;
}


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

int main(int argc, char **argv) {
    AgiGameT   game;
    AgiPicT    pic;
    int16_t    picId;
    uint8_t   *bytes;
    uint16_t   length;
    uint32_t   nonBgVisual;
    uint32_t   nonBgPriority;
    uint32_t   i;
    char       visPath[256];
    char       priPath[256];

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

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

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

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

    if (!agiPicAlloc(&pic)) {
        fprintf(stderr, "FAIL: agiPicAlloc out of memory\n");
        free(bytes);
        agiResClose(&game);
        return 1;
    }
    agiPicClear(&pic);

    if (!agiPicDecode(&pic, bytes, length)) {
        fprintf(stderr, "FAIL: PIC %d decode rejected (malformed stream?)\n", (int)picId);
        agiPicFree(&pic);
        free(bytes);
        agiResClose(&game);
        return 1;
    }

    nonBgVisual   = 0u;
    nonBgPriority = 0u;
    for (i = 0u; i < (uint32_t)AGI_PIC_PIXELS; i++) {
        if (pic.visual[i] != AGI_VISUAL_BG) {
            nonBgVisual++;
        }
        if (pic.priority[i] != AGI_PRIORITY_BG) {
            nonBgPriority++;
        }
    }

    snprintf(visPath, sizeof(visPath), "build/tests/pic%d_visual.ppm", (int)picId);
    snprintf(priPath, sizeof(priPath), "build/tests/pic%d_priority.pgm", (int)picId);
    if (!writePpm(visPath, pic.visual) || !writePgm(priPath, pic.priority)) {
        fprintf(stderr, "FAIL: could not write output images\n");
        agiPicFree(&pic);
        free(bytes);
        agiResClose(&game);
        return 1;
    }

    printf("PIC %d: %u bytes, visual %u/%u px painted, priority %u/%u px painted\n",
           (int)picId, (unsigned)length,
           (unsigned)nonBgVisual, (unsigned)AGI_PIC_PIXELS,
           (unsigned)nonBgPriority, (unsigned)AGI_PIC_PIXELS);
    printf("  wrote %s\n", visPath);
    printf("  wrote %s\n", priPath);

    agiPicFree(&pic);
    free(bytes);
    agiResClose(&game);
    return 0;
}
