/*
 * gfx_dbuf — double-buffering demo.
 *
 * Renders a moving rectangle by alternating between page 0 and page 1:
 *   - Each frame: draw to the HIDDEN page, then flip visible to it.
 *   - The page currently being displayed is never written to, so the
 *     user only sees fully-rendered frames (no half-drawn artefacts,
 *     no flicker from the per-frame clear).
 *
 * Indicators help verify the swap is actually happening:
 *   - A small colour swatch in the top-right alternates each frame:
 *     one shade when page 0 is shown, another when page 1.  If the
 *     box motion looks smooth and the swatch flickers between the two
 *     shades at the animation rate, the swap is working.
 *
 * NOTE: each graphics screen has its OWN palette page (screen 0 →
 * palette 0, screen 1 → palette 1).  We load the same palette into
 * both so the colours look identical regardless of which page is
 * currently visible.  See memory/sprinter_graphics.md.
 *
 * Press any key to exit.
 */

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

static uint8_t palette[256 * 4];

#define COL_BLACK   0x00
#define COL_BG      0x20
#define COL_STRIPE  0x60
#define COL_TEXT    0xC0
#define COL_BOX     0xFF
#define COL_PAGE0   0x40
#define COL_PAGE1   0xE0

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;
    }
}

static void draw_frame(int box_x, int box_y, uint8_t page_indicator)
{
    gfx_clear256(COL_BG);

    /* Static decoration — identical on both pages so the swap doesn't
     * flicker the chrome. */
    gfx_fill_rect256(0, 0,              GFX_WIDTH, 8, COL_STRIPE);
    gfx_fill_rect256(0, GFX_HEIGHT - 8, GFX_WIDTH, 8, COL_STRIPE);

    gfx_text256(8, 16, "double-buffering demo", COL_TEXT, COL_BG);
    gfx_text256(8, GFX_HEIGHT - 24,
             "press a key to exit", COL_TEXT, COL_BG);

    /* Page indicator — different shade for each page so a flickering
     * box here proves the swap is happening. */
    gfx_fill_rect256(GFX_WIDTH - 24, 16, 12, 12,
                  page_indicator ? COL_PAGE1 : COL_PAGE0);

    /* The animated box. */
    gfx_fill_rect256(box_x, box_y, 40, 30, COL_BOX);
    gfx_rect256    (box_x, box_y, 40, 30, COL_BLACK);
}

int main(void)
{
    make_palette();
    uint8_t prev = gfx_init(GFX_MODE_320x256x256, 0);

    /* Each graphics screen has its own palette page (0→pal 0, 1→pal 1).
     * Load the same data into both so the visual swap is seamless. */
    gfx_pal_load(0, 0, 0, palette);
    gfx_pal_load(1, 0, 0, palette);

    /* Clear both pages so the very first flip doesn't reveal garbage. */
    gfx_set_draw_page(0);
    gfx_clear256(COL_BG);
    gfx_set_draw_page(1);
    gfx_clear256(COL_BG);

    /* Bouncing rect state. */
    int box_x  = 10;
    int box_y  = 100;
    int dx     = 3;
    int dy     = 2;
    const int box_w = 40, box_h = 30;

    while (!kbhit()) {
        uint8_t hidden = 1 - gfx_get_visible_page();
        gfx_set_draw_page(hidden);

        draw_frame(box_x, box_y, hidden);

        /* Wait for the next frame interrupt before flipping, so the
         * page swap lands during vertical retrace and the user never
         * sees a half-drawn frame. */
        gfx_wait_vsync();
        gfx_set_visible_page(hidden);

        /* Step the box; bounce off all four walls. */
        box_x += dx;
        if (box_x < 0)                    { box_x = 0;                    dx = -dx; }
        if (box_x + box_w > GFX_WIDTH)    { box_x = GFX_WIDTH  - box_w;   dx = -dx; }
        box_y += dy;
        if (box_y < 8)                    { box_y = 8;                    dy = -dy; }
        if (box_y + box_h > GFX_HEIGHT-8) { box_y = GFX_HEIGHT-8 - box_h; dy = -dy; }
    }

    (void)getch();
    gfx_done(prev);
    puts("done");
    return 0;
}
