/*
 * gfx_dbuf — double-buffering demo (BGI, bgi256.lib).
 *
 * Рендерит движущийся прямоугольник, чередуя страницу 0 и 1:
 *   - Каждый кадр: рисуем на СКРЫТОЙ странице, затем делаем её видимой.
 *   - Видимая страница никогда не пишется → пользователь видит только
 *     целые кадры (без артефактов, без мерцания от per-frame clear).
 *
 * Переключение страниц и vsync — через mode-agnostic gfx_* (gfx.h,
 * остались в BGI_GFX): gfx_set_draw_page / gfx_set_visible_page /
 * gfx_get_visible_page / gfx_wait_vsync.  Рисование — через BGI
 * (graphics.h): cleardevice / bar / rectangle / outtextxy.  Режим
 * 0x81 выбирается линковкой: EXTRA_FLAGS=--gfx 256.
 *
 * Индикатор страницы (маленький swatch сверху-справа) меняет оттенок
 * каждый кадр — если бокс движется плавно, а swatch мигает между двумя
 * оттенками, своп работает.  Press any key to exit.
 */

#include <stdio.h>
#include <conio.h>
#include <graphics.h>
#include <gfx.h>          /* double-buffering + vsync (mode-agnostic) */
#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)
{
    setbkcolor(COL_BG); cleardevice();

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

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

    /* Page indicator — different shade per page: flicker here proves
     * the swap is happening. */
    setfillstyle(SOLID_FILL, page_indicator ? COL_PAGE1 : COL_PAGE0);
    bar(GFX_WIDTH - 24, 16, GFX_WIDTH - 24 + 11, 16 + 11);

    /* The animated box. */
    setfillstyle(SOLID_FILL, COL_BOX);
    bar(box_x, box_y, box_x + 39, box_y + 29);
    setcolor(COL_BLACK);
    rectangle(box_x, box_y, box_x + 39, box_y + 29);
}

int main(void)
{
    make_palette();
    initgraph();

    /* Каждая графическая страница имеет свою палитру (0→pal 0, 1→pal 1).
     * Грузим одну и ту же в обе — визуальный своп бесшовный. */
    gfx_pal_load(0, 0, 0, palette);
    gfx_pal_load(1, 0, 0, palette);

    /* Очистить обе страницы, чтобы первый флип не вскрыл мусор. */
    setbkcolor(COL_BG);
    gfx_set_draw_page(0); cleardevice();
    gfx_set_draw_page(1); cleardevice();

    /* 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);

        /* Ждать следующий кадр перед флипом — своп падает в vblank,
         * пользователь не видит полунарисованный кадр. */
        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();
    closegraph();
    puts("done");
    return 0;
}
