/*
 * sprites — тест Фазы B спрайтового API (docs/sprite-api-design.md §3):
 * putsprite / movesprite / gfx_heal / gfx_blit_part поверх accel-ядра.
 *
 * Сцена: полосатый фон (вертикальные полосы 8px RED/LIGHTGRAY, банк
 * 0x50 → фон и в VRAM, и в ОЗУ-копии).  Спрайт — шар 24×24 (YELLOW с
 * RED-ободом), углы = 0xFF (GFX_TRANSPARENT).
 *
 * Стадии (паузы по RTC, скриншот после каждой):
 *   S1  фон + разметка.
 *   S2  putsprite:
 *        - в центре зоны (C) — прозрачные углы: полосы сквозь них;
 *        - 4 клип-позиции: слева x=-12, справа x=308, сверху y=-10,
 *          снизу y=244 — рисуется только видимая часть, соседняя
 *          память не портится;
 *        - heal-проба: putsprite в H=(60,130) и тут же gfx_heal того же
 *          rect — шара БЫТЬ НЕ ДОЛЖНО (первая проверка heal src==dst
 *          через акселератор — риск §10.4 дизайна);
 *        - атлас: gfx_blit_part кадра 1 (MAGENTA-шар) из ленты 2×24×24
 *          в (270,130) банком GFX_BANK_SPRITE.
 *       Проверка A (по ОЗУ, getpixel = ОЗУ-копия): полосы под C целы.
 *   S3  movesprite: шар из (16,60) 12 шагов по (+16,+8) до (208,156) —
 *       след обязан быть чистым (heal старой позиции на каждом шаге).
 *       Проверка B: ОЗУ вдоль пути — полосы.
 *
 * VRAM программно не читается (write-only) — корректность экрана
 * подтверждают скриншоты; программные проверки ловят порчу ОЗУ-копии.
 *
 * Запуск:
 *   python3 toolchain/mame_interactive.py tests/sprites/sprites.exe \
 *       --snap 13,17,21 --timeout 23
 */

#include <graphics.h>
#include <gfx.h>
#include <time.h>

#define BW 24                   /* сторона спрайта */
#define BG_Y0 40
#define BG_Y1 199

#define CX 150                  /* центральный putsprite */
#define CY 100

static uint8_t ball[4 + BW * BW];         /* YELLOW шар */
static uint8_t strip[4 + 2 * BW * BW];    /* лента: кадр0 YELLOW, кадр1 MAGENTA */

static uint8_t stripe_color(int x)
{
    return ((x >> 3) & 1) ? LIGHTGRAY : RED;
}

/* Шар: r<=9 — fill, r 9..11 — ring, дальше прозрачно (0xFF). */
static uint8_t ball_pixel(int x, int y, uint8_t fill, uint8_t ring)
{
    int dx = x - BW / 2, dy = y - BW / 2;
    int d2 = dx * dx + dy * dy;

    if (((x == 0 || x == BW - 1) && ((y >=0 && y <= 3) || (y >=BW - 4 && y <= BW - 1))) || 
        ((y == 0 || y == BW - 1) && ((x >=0 && x <= 3) || (x >=BW - 4 && x <= BW - 1)))) return GREEN;
    if (x==0) return GFX_TRANSPARENT;
    if (d2 <= 81)  return fill;
    if (d2 <= 121) return ring;
    return GFX_TRANSPARENT;
}

static void build_images(void)
{
    uint8_t *p;
    int x, y, f;

    p = ball;
    *p++ = BW; *p++ = 0; *p++ = BW; *p++ = 0;
    for (y = 0; y < BW; y++)
        for (x = 0; x < BW; x++)
            *p++ = ball_pixel(x, y, YELLOW, RED);

    /* Лента 48×24: кадры лежат рядом по x (кадр N: sx = N*BW). */
    p = strip;
    *p++ = 2 * BW; *p++ = 0; *p++ = BW; *p++ = 0;
    for (y = 0; y < BW; y++)
        for (f = 0; f < 2; f++)
            for (x = 0; x < BW; x++)
                *p++ = ball_pixel(x, y, f ? LIGHTMAGENTA : YELLOW,
                                  f ? BLUE : RED);
}

/* ОЗУ-копия (getpixel) в трёх точках зоны 24×24 — нетронутые полосы? */
static uint8_t ram_is_stripes(int zx, int zy)
{
    static const uint8_t off[3][2] = { {12, 12}, {8, 16}, {16, 6} };
    uint8_t i;

    for (i = 0; i < 3; i++) {
        int x = zx + off[i][0];
        if (getpixel(x, zy + off[i][1]) != stripe_color(x))
            return 0;
    }
    return 1;
}

static void pause_sec(uint8_t n)
{
    datetime_t dt;
    uint8_t last;

    getdatetime(&dt);
    last = dt.second;
    while (n) {
        getdatetime(&dt);
        if (dt.second != last) {
            last = dt.second;
            n--;
        }
    }
}

static void say(int y, const char *s)
{
    outtextxy(4, y, s);
}

int main(void)
{
    uint8_t chkA, chkB;
    int x, i;

    build_images();
    initgraph();
    cleardevice();
    gfx_sprite_clip(1);
    setcolor(WHITE);

    /* ---- S1: фон -------------------------------------------------- */
    say(4, "SPRITES: putsprite/movesprite/heal");
    for (x = 0; x < 320; x += 8) {
        setfillstyle(SOLID_FILL, stripe_color(x));
        bar(x, BG_Y0, x + 7, BG_Y1);
    }
    say(208, "S1 bg");
    (void)getchar();

    /* ---- S2: putsprite + клипы + heal-проба + атлас ---------------- */
    putsprite(CX, CY, ball);            /* центр: прозрачные углы     */
    (void)getchar();
    putsprite(-12, 52, ball);           /* клип слева                 */
    (void)getchar();
    putsprite(308, 82, ball);           /* клип справа                */
    (void)getchar();
    putsprite(297, 162, ball);           /* клип справа                */
    (void)getchar();
    putsprite(60, -10, ball);           /* клип сверху                */
    (void)getchar();
    putsprite(290, 244, ball);          /* клип снизу                 */
    (void)getchar();
    putsprite(60, 130, ball);           /* heal-проба: нарисовать...  */
    (void)getchar();
    gfx_heal(60, 130, BW, BW);          /* ...и стереть (шара НЕТ)    */
    (void)getchar();
    gfx_set_bank(GFX_BANK_SPRITE);      /* атлас: кадр 1 (MAGENTA)    */
    (void)getchar();
    gfx_blit_part(270, 130, strip, BW, 0, BW, BW);
    (void)getchar();
    gfx_set_bank(GFX_BANK_NORMAL);
    (void)getchar();

    chkA = ram_is_stripes(CX, CY);
    say(218, "S2 putsprite+clip, heal@60,130");
    say(228, chkA ? "A RAM under sprite: PASS"
                  : "A RAM under sprite: FAIL");
    pause_sec(4);

    /* ---- S3: movesprite — след обязан быть чистым ------------------ */
    x = 16;
    putsprite(x, 60, ball);
    (void)getchar();
    for (i = 0; i < 12; i++) {
        movesprite(x, 60 + (x - 16) / 2, x + 16, 60 + (x - 16 + 16) / 2,
                   ball);
        x += 16;
        (void)getchar();
    }
    chkB = ram_is_stripes(16, 60) && ram_is_stripes(112, 108);
    say(238, chkB ? "B RAM along path:   PASS"
                  : "B RAM along path:   FAIL");
    say(248, "S3 moved 16,60 -> 208,156 done");

    (void)getchar();
}
