// Tiles: 8x8 pixel blocks aligned on the 8-pixel grid of any surface.
//
// A "tile" in JoeyLib isn't a separate object -- it's just the 8x8
// region of a jlSurfaceT at block coordinates (bx, by), where bx is in
// [0, 39] and by is in [0, 24] (40x25 blocks per 320x200 surface).
// The tile API is a small set of operations that move 32-byte chunks
// between surfaces or fill them with a solid color.
//
// Why this shape: anything you can do with a regular surface --
// jlDrawPixel, jlDrawLine, jlFillRect, blits -- also works on tile-aligned
// regions, so authors can paint, edit, and procedurally generate tile
// content using the same primitives they use for everything else.
// "Fonts," "terrain tilesets," and "spritesheets" are just surfaces
// you treat as tile sources at draw time.
//
// Snap / paste use a small jlTileT value type so callers don't need a
// 32 KB scratch surface for save-under-style work.
//
// Block coords map to pixels by multiplying by 8 -- byte-aligned in
// 4bpp packed (8 px = 4 bytes per row), so all the operations are
// byte-shoveling memcpys with no shifting.

#ifndef JOEYLIB_TILE_H
#define JOEYLIB_TILE_H

#include "platform.h"
#include "surface.h"
#include "types.h"

// ----- Constants -----

#define TILE_PIXELS_PER_SIDE  8
#define TILE_BYTES_PER_ROW    4
#define TILE_BYTES            (TILE_BYTES_PER_ROW * TILE_PIXELS_PER_SIDE)
#define TILE_BLOCKS_PER_ROW   (SURFACE_WIDTH  / TILE_PIXELS_PER_SIDE)
#define TILE_BLOCKS_PER_COL   (SURFACE_HEIGHT / TILE_PIXELS_PER_SIDE)

// Sentinel for asciiMap entries that should not draw. jlDrawText
// advances the cursor past TILE_NO_GLYPH chars without writing.
#define TILE_NO_GLYPH         ((uint16_t)0xFFFFu)

// ----- Types -----

// Stack-allocated 32-byte snapshot buffer for jlTileSnap / jlTilePaste.
typedef struct jlTileT {
    uint8_t pixels[TILE_BYTES];
} jlTileT;

// ----- API -----

// Copy the 8x8 block at (srcBx, srcBy) on src to (dstBx, dstBy) on
// dst, opaque. Out-of-range block coordinates on either side are
// silent no-ops; src and dst can be the same surface.
void jlTileCopy(jlSurfaceT *dst, uint8_t dstBx, uint8_t dstBy,
              const jlSurfaceT *src, uint8_t srcBx, uint8_t srcBy);

// Like jlTileCopy but pixels equal to transparentIndex are skipped --
// the destination pixel keeps its original value. Use this for fonts
// (transparentIndex = 0 leaves the page background showing through
// glyph backgrounds) and for any tileset where some pixels are meant
// to be see-through.
void jlTileCopyMasked(jlSurfaceT *dst, uint8_t dstBx, uint8_t dstBy,
                    const jlSurfaceT *src, uint8_t srcBx, uint8_t srcBy,
                    uint8_t transparentIndex);

// Fill the 8x8 block at (bx, by) with a solid color. Equivalent to
// jlFillRect(s, bx*8, by*8, 8, 8, colorIndex) but skips the rect
// clipping math since tile coords are already known to be in range.
void jlTileFill(jlSurfaceT *s, uint8_t bx, uint8_t by, uint8_t colorIndex);

// Load up to maxTiles tiles from a baked .tbk file produced by
// tools/assetbake/assetbake.py. The file's per-target byte is checked
// against the build platform and the loader refuses mismatches.
//
// outValid (optional, NULL OK) is set to true for each loaded slot
// so the caller can distinguish loaded tiles from any leftover slots
// in a fixed-size jlTileT[] array.
//
// outPalette (optional, NULL OK) receives the 16-entry $0RGB LE16
// palette if the file embeds one.
//
// Returns the number of tiles actually written. Returns 0 if the
// file is missing, the magic / target byte is wrong, or maxTiles is 0.
uint16_t jlTileBankLoad(const char *path, jlTileT *outTiles, uint16_t maxTiles,
                        bool *outValid, uint16_t *outPalette);

// Stream a baked .tbk straight onto a surface's 8x8 block grid: tile i
// lands at block (i % TILE_BLOCKS_PER_ROW, i / TILE_BLOCKS_PER_ROW),
// the same row-major layout the bake tools author against. Use this
// instead of jlTileBankLoad + a jlTileT[] when the bank is too big for
// a caller-side buffer -- a full 1000-tile font sheet needs 32000
// bytes, which no IIgs example can heap-allocate (the clang libc heap
// is a few hundred bytes) or hold in bank-0 BSS. One file pass with a
// small internal scratch; per-target/magic validation and outPalette
// behave exactly like jlTileBankLoad. Blocks beyond the file's tile
// count keep their prior surface content. Returns tiles pasted.
uint16_t jlTileBankLoadToSurface(const char *path, jlSurfaceT *dst, uint16_t *outPalette);

// Capture the 8x8 block at (bx, by) into the caller's jlTileT. Used
// for save-under style work where allocating a scratch surface would
// be overkill.
void jlTileSnap(const jlSurfaceT *src, uint8_t bx, uint8_t by, jlTileT *out);

// Paste a jlTileT back onto a surface at block (bx, by). Always
// opaque; use jlTileCopyMasked if you need transparency.
void jlTilePaste(jlSurfaceT *dst, uint8_t bx, uint8_t by, const jlTileT *in);

// Paste a jlTileT treating the source as a 1-bpp silhouette: any
// non-zero source pixel renders as fgColor on dst, zero pixels
// render as bgColor. Useful when the tile bitmap is a fixed glyph
// (e.g. a character cell) but the per-cell color comes from a
// separate map (e.g. C64-style color RAM). Both colors are masked
// to 4 bits. Opaque -- if you need transparent background, do
// jlTileFill(bg) first then mask separately, or follow up with a
// jlTileCopyMasked call.
void jlTilePasteMono(jlSurfaceT *dst, uint8_t bx, uint8_t by,
                     const jlTileT *in, uint8_t fgColor, uint8_t bgColor);

// Draw a NUL-terminated ASCII string at block (bx, by) using glyphs
// pulled from fontSurface.
//
// asciiMap is a 256-entry table mapping ASCII code to glyph location
// on fontSurface, encoded as a packed uint16_t: low byte = source
// blockX, high byte = source blockY. asciiMap[c] == TILE_NO_GLYPH
// causes that character to be skipped (cursor advances, nothing
// drawn). Glyph color 0 is treated as transparent so the underlying
// surface shows through glyph backgrounds.
//
// The cursor wraps to the next row at the right edge and truncates
// at the bottom edge.
void jlDrawText(jlSurfaceT *dst, uint8_t bx, uint8_t by,
              const jlSurfaceT *fontSurface, const uint16_t *asciiMap,
              const char *str);

// Map entry value that leaves a cell untouched (nothing pasted, the
// destination shows through). Mirrors TILE_NO_GLYPH's role in
// jlDrawText.
#define TILE_MAP_SKIP         ((uint8_t)0xFFu)

// Paste a wTiles x hTiles rectangle of tiles in ONE call: the
// validation, dispatch, and dirty union are paid once for the whole
// map instead of per tile (the jlDrawText batching pattern
// generalized, NATIVE-PERF Phase 3). `tiles` is the tileset; `map`
// holds wTiles * hTiles tileset indices in row-major order
// (map[row * wTiles + col]). An index of TILE_MAP_SKIP leaves that
// cell untouched; any other out-of-tileset index is the CALLER'S
// contract to avoid (the batch does not know the tileset length and
// does not range-check it). The whole rectangle must fit on the
// surface -- a map whose edge crosses TILE_BLOCKS_PER_ROW/COL is
// rejected as a no-op, matching the per-tile ops' silent-reject
// convention. wTiles or hTiles of 0 is a no-op.
void jlTileMapPaste(jlSurfaceT *dst, uint8_t bx, uint8_t by, uint8_t wTiles, uint8_t hTiles, const jlTileT *tiles, const uint8_t *map);

#endif
