/*
 * gfx_mouse_test — exercises the graphics-mode mouse cursor API:
 *   $09 LOAD  CURSOR    — install a custom cursor bitmap
 *   $81 CHANGE VIDEO MODE — let the driver re-sync after switching modes
 *   $0B RETURN CURSOR   — read the cursor back (may be a stub in MAME)
 *
 * Flow:
 *   1. Init the mouse driver in text mode.
 *   2. Switch to 320x256x256, notify the driver.
 *   3. Build an 8x8 cursor — solid white border + transparent middle —
 *      and load it.  Show it.  Move the mouse to verify it tracks.
 *   4. Press a key: query the driver's view of the cursor and dump the
 *      result so we can compare against what we loaded.
 *   5. Hide cursor, return to text mode, print the round-trip dump.
 *
 * The driver's bitmap format is undocumented — best guess is one byte
 * per pixel (palette index, with 0 meaning transparent).  We load 0xFF
 * for opaque cells and 0 for transparent so the cursor stands out
 * against any background.
 */

#include <stdio.h>
#include <conio.h>
#include <mouse.h>
#include <gfx.h>
#include <stdint.h>

#define CURSOR_W   12
#define CURSOR_H   12
#define CURSOR_BUF_BYTES  (CURSOR_W * CURSOR_H)

/* Cursor format (verified empirically 2026-05-31):
 *   ONE BYTE per pixel — row-major, width*height bytes total.
 *   0xFF = transparent (background shows through)
 *   any other byte = palette index (drawn opaque)
 *
 * Cursor bitmap lives in a dedicated video bank, not the 0x50 page used
 * by gfx_clear256 / gfx_putpixel256 — that's why 0xFF reads as transparent
 * even though in the main visible page 0xFF is a regular colour.
 */

static uint8_t cursor_bitmap[CURSOR_BUF_BYTES];
static uint8_t roundtrip_buf[CURSOR_BUF_BYTES];
static uint8_t palette[256 * 4];

/* Arrow shape, 1 byte per pixel.  Each cell is either:
 *     _   transparent (0xFF, lets the background through)
 *     O   black solid pixel (0x00)
 *     W   white solid pixel (0xFF would mean transparent, so use 0xFF
 *         palette entry by hand if you want a white pixel — for the
 *         default greyscale palette in this demo, W = bright grey 0xE0)
 *
 * Feel free to edit the 12×12 grid below to draw a nicer cursor. */
#define _ 0xFF
#define O 0x00
#define W 0xE0
static const uint8_t cursor_arrow[CURSOR_BUF_BYTES] = {
    O,_,_,_,_,_,_,_,_,_,_,_,
    O,O,_,_,_,_,_,_,_,_,_,_,
    O,W,O,_,_,_,_,_,_,_,_,_,
    O,W,W,O,_,_,_,_,_,_,_,_,
    O,W,W,W,O,_,_,_,_,_,_,_,
    O,W,W,W,W,O,_,_,_,_,_,_,
    O,W,W,W,W,W,O,_,_,_,_,_,
    O,W,W,W,O,O,O,O,_,_,_,_,
    O,O,O,W,O,_,_,_,_,_,_,_,
    O,_,_,O,W,O,_,_,_,_,_,_,
    _,_,_,O,W,O,_,_,_,_,_,_,
    _,_,_,_,O,O,_,_,_,_,_,_,
};
#undef _
#undef O
#undef W

static void make_palette(void)
{
    for (int i = 0; i < 256; i++) {
        palette[i*4+0] = (uint8_t)i;
        palette[i*4+1] = (uint8_t)i;
        palette[i*4+2] = (uint8_t)i;
        palette[i*4+3] = 0;
    }
}

/* Copy the arrow pattern into our scratch (one place for the driver
 * to point IX at). */
static void build_cursor(void)
{
    for (int i = 0; i < CURSOR_BUF_BYTES; i++)
        cursor_bitmap[i] = cursor_arrow[i];
}

static void wait_key(void)
{
    while (!kbhit()) { /* spin */ }
    (void)getch();
}

int main(void)
{
    textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
    clrscr();
    cputs("gfx_mouse_test\r\n");

    if (mouse_init() < 0) {
        cputs("mouse_init failed - no driver\r\n");
        return 1;
    }
    mouse_set_sensitivity(2, 2);

    /* --- enter graphics, load custom cursor, then show -------------- */
    make_palette();
    build_cursor();

    uint8_t prev = gfx_init(GFX_MODE_320x256x256, 0);
    gfx_pal_load(0, 0, 0, palette);
    gfx_clear256(0x40);                          /* dim grey background */

    /* Coordinate grid every 32 px so the cursor position reads easily. */
    for (int x = 0; x < GFX_WIDTH;  x += 32) gfx_vline256(x, 0, GFX_HEIGHT, 0x80);
    for (int y = 0; y < GFX_HEIGHT; y += 32) gfx_hline256(0, y, GFX_WIDTH,  0x80);

    mouse_video_mode_changed(GFX_MODE_320x256x256);
    mouse_bounds_x(0, GFX_WIDTH  - 1);
    mouse_bounds_y(0, GFX_HEIGHT - 1);

    mouse_cursor_t custom = {
        .image  = cursor_bitmap,
        .width  = CURSOR_W,
        .height = CURSOR_H,
        .hot_x  = 0,
        .hot_y  = 0,
    };
    mouse_load_cursor(&custom);
    mouse_show();
    wait_key();

    /* --- query driver to see what it has now ------------------------ */
    mouse_cursor_t readback = {
        .image  = roundtrip_buf,
        .width  = 0, .height = 0, .hot_x = 0, .hot_y = 0,
    };
    mouse_get_cursor(&readback);

    mouse_hide();
    gfx_done(prev);
    mouse_video_mode_changed(prev);     /* tell driver we're back */

    /* --- back in text mode: report what the driver returned --------- */
    textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
    clrscr();
    cprintf("loaded   : %ux%u  hot (%u,%u)\r\n",
            CURSOR_W, CURSOR_H, 0, 0);
    cprintf("read back: %ux%u  hot (%u,%u)\r\n",
            readback.width, readback.height, readback.hot_x, readback.hot_y);

    uint16_t n = (uint16_t)readback.width * (uint16_t)readback.height;
    if (n > CURSOR_BUF_BYTES) n = CURSOR_BUF_BYTES;
    cprintf("bitmap (%u bytes):\r\n", n);
    for (uint16_t i = 0; i < n; i++) {
        cprintf("%02X ", roundtrip_buf[i]);
        if ((i % readback.width) == readback.width - 1) cputs("\r\n");
    }
    cputs("\r\n");

    cputs("press any key to exit");
    wait_key();
    return 0;
}
