// Space Taxi -- level loader.
//
// Reads a level .dat file produced by `tools/spacetaxi/mkLevel.py`
// (see assets/levels/format.md for the byte layout).
//
// A level file is small (~2-3 KB raw, plus a name + per-pad config),
// loaded once per scene change. Read fully into RAM; tilemap + colormap
// stay inside the StLevelT struct for the life of the game state.

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

#include "joey/file.h"
#include "spacetaxi.h"


// STL3: per-pad record is (tileX, tileY, tileW, standX) where standX is the
// passenger stand column in screen pixels; the STL2 per-pad letter and the
// whole fare list are gone (fares are RNG-chosen at runtime, not stored).
#define ST_LEVEL_MAGIC0  'S'
#define ST_LEVEL_MAGIC1  'T'
#define ST_LEVEL_MAGIC2  'L'
#define ST_LEVEL_MAGIC3  '3'


static bool readByte(FILE *fp, uint8_t *out) {
    int c = fgetc(fp);
    if (c == EOF) {
        return false;
    }
    *out = (uint8_t)c;
    return true;
}


static bool readBytes(FILE *fp, void *dst, size_t n) {
    return fread(dst, 1, n, fp) == n;
}


bool stLevelLoad(StLevelT *out, const char *path) {
    FILE     *fp;
    uint8_t   hdr[4];
    uint8_t   nameLen;
    uint8_t   i;
    size_t    cells;

    memset(out, 0, sizeof(*out));

    // path is a name relative to DATA/ (e.g. "levels/title.dat").
    fp = jlDataOpen(path, "rb");
    if (fp == NULL) {
        return false;
    }

    if (!readBytes(fp, hdr, 4) ||
        hdr[0] != ST_LEVEL_MAGIC0 || hdr[1] != ST_LEVEL_MAGIC1 ||
        hdr[2] != ST_LEVEL_MAGIC2 || hdr[3] != ST_LEVEL_MAGIC3) {
        fclose(fp);
        return false;
    }

    if (!readByte(fp, &nameLen) || nameLen >= sizeof(out->name)) {
        fclose(fp);
        return false;
    }
    if (!readBytes(fp, out->name, nameLen)) {
        fclose(fp);
        return false;
    }
    out->name[nameLen] = '\0';

    if (!readByte(fp, &out->tileBankId)     ||
        !readByte(fp, &out->musicId)        ||
        !readByte(fp, &out->bgColor)        ||
        !readByte(fp, &out->borderColor)    ||
        !readByte(fp, &out->taxiSpawnTileX) ||
        !readByte(fp, &out->taxiSpawnTileY)) {
        fclose(fp);
        return false;
    }

    {
        uint8_t  xGravByte;
        uint8_t  yGravByte;
        if (!readByte(fp, &out->xAccel)     ||
            !readByte(fp, &out->yAccel)     ||
            !readByte(fp, &xGravByte)       ||
            !readByte(fp, &yGravByte)) {
            fclose(fp);
            return false;
        }
        out->xGrav = (int8_t)xGravByte;
        out->yGrav = (int8_t)yGravByte;
    }
    if (!readByte(fp, &out->bgColor1)     ||
        !readByte(fp, &out->bgColor2)     ||
        !readByte(fp, &out->bgColor3)     ||
        !readByte(fp, &out->spriteMc0)    ||
        !readByte(fp, &out->spriteMc1)    ||
        !readByte(fp, &out->sprite0Color) ||
        !readByte(fp, &out->sprite1Color)) {
        fclose(fp);
        return false;
    }

    if (!readByte(fp, &out->padCount) || out->padCount > ST_MAX_PADS) {
        fclose(fp);
        return false;
    }
    for (i = 0u; i < out->padCount; i++) {
        if (!readByte(fp, &out->pads[i].tileX)  ||
            !readByte(fp, &out->pads[i].tileY)  ||
            !readByte(fp, &out->pads[i].tileW)  ||
            !readByte(fp, &out->pads[i].standX)) {
            fclose(fp);
            return false;
        }
    }

    cells = (size_t)ST_TILEMAP_W * (size_t)ST_PLAYFIELD_ROWS;
    if (!readBytes(fp, out->tilemap, cells) ||
        !readBytes(fp, out->colormap, cells)) {
        fclose(fp);
        return false;
    }

    fclose(fp);
    return true;
}


