/*
 * gfx.h — Sprinter graphics primitives.
 *
 * Two main modes:
 *   GFX_MODE_320x256x256 (0x81) — one byte per pixel, palette of 256
 *                                 entries.  API functions suffixed _256.
 *   GFX_MODE_640x256x16  (0x82) — 4 bits per pixel, palette of 16.
 *                                 API functions suffixed _16.
 *
 * Common API (no suffix) covers things that are mode-agnostic:
 *   gfx_init / gfx_done
 *   gfx_set_visible_page / gfx_set_draw_page / gfx_set_bank
 *   gfx_wait_vsync
 *   gfx_pal_load / gfx_pal_set
 *   gfx_load_default_font / gfx_set_font
 *
 * Addressing reminder:
 *   pixel (x, y) lives at CPU 0xC000 + (x or x/2) with Port_Y (0x89) = y;
 *   the gfx code maps a 16 KB VRAM page into W3 around every write.
 *   For double-buffering, page 1 starts 320 bytes later (0xC140).
 *
 * Palette: BIOS $A4 (RST 8); 4 bytes per entry — B, G, R, pad.
 */

#ifndef GFX_H
#define GFX_H

#include <stdint.h>

/* ESTEX SETVMOD codes — same values as the SETVMOD `A` register. */
#define GFX_MODE_TEXT_40x32       0x02
#define GFX_MODE_TEXT_80x32       0x03
#define GFX_MODE_320x256x256      0x81
#define GFX_MODE_640x256x16       0x82

/* Pixel dimensions of mode 0x81 (320×256, 256 colours). */
#define GFX_WIDTH   320
#define GFX_HEIGHT  256

/* Pixel dimensions of mode 0x82 (640×256, 16 colours).  Each byte at
 * 0xC000+x_byte holds two pixels: high nibble = LEFT (even-x), low
 * nibble = RIGHT (odd-x) — see memory/sprinter_graphics.md. */
#define GFX_WIDTH_16   640
#define GFX_HEIGHT_16  256
#define GFX_COLORS_16  16

/* ---- Setup / teardown -------------------------------------------- *
 *
 * Switch to `mode`, returning the previous mode for restore.  `page`
 * (0 or 1) selects the initial graphics screen — both the visible and
 * the draw page are set to it, and the W3 bank is reset to 0x50 (the
 * canonical visible video page).  Programs that don't double-buffer
 * just pass 0. */
uint8_t gfx_init(uint8_t mode, uint8_t page);

/* Restore a previously-saved video mode. */
void    gfx_done(uint8_t mode);

/* ---- Page selection (double-buffering) and bank control ---------- *
 *
 * The Sprinter hardware holds two graphics screens.  The VISIBLE page
 * is what's shown on screen; the DRAW page is where the gfx_* writes
 * land.  Render the next frame into the hidden page and flip when ready.
 *
 * "Bank" is the W3 page byte (0x50..0x5F) — bits 2,3 select
 * normal/temp/transparent display modes.  All gfx_* primitives map
 * THIS bank into W3 before touching 0xC000+.  Default is 0x50. */

void    gfx_set_visible_page(uint8_t page);   /* 0 or 1 */
uint8_t gfx_get_visible_page(void);

void    gfx_set_draw_page(uint8_t page);      /* 0 or 1 */
uint8_t gfx_get_draw_page(void);

void    gfx_set_bank(uint8_t bank);           /* 0x50..0x5F */
uint8_t gfx_get_bank(void);

/* Block until the next frame interrupt (50 Hz on Sprinter).  Uses
 * `EI; HALT` — the Z80 sleeps until the next IM2 tick that DSS programs
 * for keyboard / cursor handling.  Typical use:
 *
 *   gfx_set_draw_page(hidden);
 *   draw_frame(...);
 *   gfx_wait_vsync();              // wait for vretrace
 *   gfx_set_visible_page(hidden);  // tear-free flip
 */
void    gfx_wait_vsync(void);

/* ---- 320×256×256 (mode 0x81) drawing API -------------------------- *
 * Colour args are palette indices 0..255. */

void    gfx_clear256    (uint8_t color);
void    gfx_putpixel256 (int x, int y, uint8_t color);
void    gfx_hline256    (int x, int y, int len, uint8_t color);
void    gfx_vline256    (int x, int y, int len, uint8_t color);
void    gfx_line256     (int x0, int y0, int x1, int y1, uint8_t color);
void    gfx_rect256     (int x, int y, int w, int h, uint8_t color);
void    gfx_fill_rect256(int x, int y, int w, int h, uint8_t color);

/* ---- 640×256×16 (mode 0x82) drawing API --------------------------- *
 * Colour args are palette indices 0..15.  The accelerator works
 * byte-wise so vline16 falls back to per-row RMW (a byte spans two
 * horizontal pixels). */

void    gfx_clear16    (uint8_t color);
void    gfx_putpixel16 (int x, int y, uint8_t color);
void    gfx_hline16    (int x, int y, int len, uint8_t color);
void    gfx_vline16    (int x, int y, int len, uint8_t color);
void    gfx_line16     (int x0, int y0, int x1, int y1, uint8_t color);
void    gfx_rect16     (int x, int y, int w, int h, uint8_t color);
void    gfx_fill_rect16(int x, int y, int w, int h, uint8_t color);

/* ---- Bitmap-font text -------------------------------------------- *
 * Font is 256 glyphs × 8 rows × 1 byte (ZX-Spectrum format), 2 KB.
 * On first use the default system font is fetched via BIOS WIN_GET_ZG
 * (fn 0xB8).  gfx_set_font() lets you swap in a custom font (the
 * pointer is held — keep the storage alive). */

void    gfx_load_default_font(void);
void    gfx_set_font(const uint8_t *font);

/* 320×256×256 text: one byte per pixel; advances x by 8 per char. */
void    gfx_putchar256(int x, int y, char c, uint8_t fg, uint8_t bg);
void    gfx_text256   (int x, int y, const char *s, uint8_t fg, uint8_t bg);

/* 640×256×16 text: 4 bits per pixel; x must be EVEN (byte-aligned). */
void    gfx_putchar16 (int x, int y, char c, uint8_t fg, uint8_t bg);
void    gfx_text16    (int x, int y, const char *s, uint8_t fg, uint8_t bg);

/* ---- Palette ----------------------------------------------------- *
 * Each graphics page has its own palette page (page 0 → palette 0,
 * page 1 → palette 1).  For seamless double-buffering, load the same
 * palette into both. */

/* Load a contiguous block of palette entries.
 *   pal_num:  0..3 (graphics palettes)
 *   start:    first colour slot (0..255)
 *   count:    number of slots (0 → 256)
 *   data:     pointer to count entries, each formatted (B, G, R, 0). */
void    gfx_pal_load(uint8_t pal_num, uint8_t start, uint8_t count,
                     const uint8_t *data);

/* Convenience: set one palette entry from RGB.  Internally builds the
 * BGR+pad triple and calls gfx_pal_load(pal_num, idx, 1, ...). */
void    gfx_pal_set (uint8_t pal_num, uint8_t idx,
                     uint8_t r, uint8_t g, uint8_t b);

/* Read a contiguous block of entries back from a graphics palette. */
void    gfx_pal_get (uint8_t pal_num, uint8_t start, uint8_t count,
                     uint8_t *data);

/* Read one entry into R, G, B pointers (any may be NULL). */
void    gfx_pal_get_color(uint8_t pal_num, uint8_t idx,
                          uint8_t *r, uint8_t *g, uint8_t *b);

/* Restore the system default graphics palette (BIOS $A6, type=1). */
void    gfx_pal_reset(void);

#endif
