// iigs/sprite.h - 16x16 fixed-shape 4bpp packed sprite primitives for
// SHR 320 mode.
//
// Phase 4.2 / Phase 0.6 standalone path: sprite.c brings up SHR 320
// mode itself (NEWVIDEO bit 7 via $C029, SCBs at $E1:9D00, palette 0
// at $E1:9E00) so callers don't have to drag startdesk()'s full 16-
// tool chain in.  640 mode deferred per Phase 0.6.
//
// Pixel format (4bpp packed, SHR 320 mode native):
//   - 128 bytes per sprite image: 16 lines x 8 bytes per line.
//   - Byte layout: high nibble = LEFT pixel, low nibble = RIGHT pixel.
//   - Pixel value 0 is TRANSPARENT (no plot, background shows through).
//     Pixel values 1..15 plot the corresponding palette-0 color.
//
// ----- $C035 SHADOW GOTCHA (CRITICAL) -----
// Bank 0 $2000..$9FFF mirrors to $E1:2000..$E1:9FFF via the IIgs SHR
// shadow register at $C035.  This means a background save buffer
// allocated in bank-0 $2000..$9FFF would alias the very SHR pixels it
// is trying to preserve.  The built-in save area lives in bank 0
// $A000..$AFFF (16 sprites x 128 bytes = 2 KB), which is OUTSIDE the
// shadowed range and safe.  If you need more than 16 sprites or want
// to relocate the save area, call iigsSpriteAttachBuffer() with a
// caller-supplied buffer that lives EITHER above $A000 in bank 0 OR in
// a non-zero bank.  Buffers inside bank-0 $2000..$9FFF will silently
// scribble on the screen.
//
// Coordinate system: (x, y) is the top-left corner of the sprite.
//   - x in pixels (0..303 for full sprite visibility at right edge).
//     Currently MUST be even (no sub-byte horizontal alignment in this
//     first cut).  Odd x is clamped down 1 pixel.
//   - y in scan lines (0..183 for full sprite visibility at bottom).
//
// Off-screen clipping is NOT implemented in this first cut.  Callers
// must place sprites entirely on-screen (x <= 304 even, y <= 184).

#ifndef IIGS_SPRITE_H
#define IIGS_SPRITE_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <stddef.h>


// Maximum sprites tracked by the built-in list.  Bumped past this only
// by calling iigsSpriteAttachBuffer() with a larger caller buffer.
#define IIGS_SPRITE_MAX_DEFAULT  16


// Each sprite is (position, pointer-to-128-bytes).  pixels points to
// the 4bpp packed image (16 lines x 8 bytes).  Pixel value 0 in the
// source is transparent.
typedef struct IigsSpriteT {
    uint16_t        x;          // top-left x, even pixel
    uint16_t        y;          // top-left scan line
    const uint8_t * pixels;     // 128 bytes, 4bpp packed
} IigsSpriteT;


// Bring up SHR 320 mode (NEWVIDEO bit 7 = 1, all SCBs = 0x00 for
// palette 0 in 320 mode, palette 0 loaded with a default 16-color
// ramp).  Idempotent: subsequent calls reset the screen state.
//
// After this returns, callers can write SHR pixel bytes directly to
// $E1:2000..$E1:9CFF, or use the sprite list API below.
void iigsSpriteInit(void);


// Set palette 0 to a caller-supplied 16-entry table.  Each entry is a
// 12-bit RGB value (0x0RGB).  Pass NULL to reset to the default ramp
// iigsSpriteInit() installed.
void iigsSpriteSetPalette(const uint16_t *palette16);


// Replace the built-in save buffer with caller-supplied storage.  buf
// MUST live OUTSIDE bank-0 $2000..$9FFF (see $C035 shadow gotcha at
// top of this header).  size must be at least sprites * 128 bytes.
// Returns the maximum number of sprites the new buffer supports
// (size / 128, capped).  Pass buf=NULL,size=0 to revert to the built-in
// 16-sprite buffer.
uint16_t iigsSpriteAttachBuffer(void *buf, size_t size);


// ----- sprite list API -----------------------------------------------
// Typical frame:
//   iigsSpriteBegin();
//   for (each sprite)  iigsSpriteAdd(&s);
//   iigsSpriteRenderAll();    // save background + blit each sprite
//   // ... game logic, animation update ...
//   iigsSpriteEraseAll();     // restore saved background in reverse
//
// Render/erase pair preserves the framebuffer outside the sprite
// rectangles.  EraseAll MUST be called before the next Begin if the
// background should not accumulate prior frames.
// ---------------------------------------------------------------------

// Clear the sprite list.  Call once at the start of each frame.
void iigsSpriteBegin(void);


// Append one sprite to the current frame's list.  Copies the sprite
// descriptor by value (caller may modify or free *s after return).
// Silently no-ops if the list is full.  Returns the slot index, or
// 0xFFFF if full.
uint16_t iigsSpriteAdd(const IigsSpriteT *s);


// Save the 16x16 background under each sprite into the save buffer,
// then blit each sprite (with pixel 0 = transparent).  Walk order:
// list order (sprite 0 first, last drawn on top).
void iigsSpriteRenderAll(void);


// Restore each saved background in REVERSE order (last sprite first)
// so overlapping sprites de-occlude correctly.  Pair with the most
// recent iigsSpriteRenderAll().
void iigsSpriteEraseAll(void);


// Count of sprites currently in the list.  Useful for tests + debug.
uint16_t iigsSpriteCount(void);


#ifdef __cplusplus
}
#endif

#endif  // IIGS_SPRITE_H
