/*
 * sprite_update — отрисовать кадр сцены на ТЕКУЩЕЙ draw-странице p
 * (gfx_get_draw_page).  Ядро batch-пасса движка v2 (см.
 * docs/sprite-api-design.md §9.1): вся работа кадра — здесь, двухпроходно
 * «heal ВСЕ → блит ВСЕ», под ОДНОЙ W3-скобкой и ОДНИМ банком на проход.
 *
 * Дисциплина двух проходов ОБЯЗАТЕЛЬНА: слияние heal+блит per-sprite не
 * работает на перекрытиях (heal соседа выкусывает только что
 * нарисованный спрайт — проверено на examples/balls 2026-07-11).
 *
 * Что перерисовывается: спрайт, у которого страница p ГРЯЗНАЯ (dbit —
 * взведён sprite_move/frame/show/hide/touch) ИЛИ флаг SPR_ALWAYS.
 * Неизменившиеся с прошлого визита этой страницы (и без SPR_ALWAYS) не
 * трогаются — суть retained-модели.
 *
 * Проход 1 (банк 0x50): heal старой позиции drawn[p] у тех, кто нарисован
 * (drawn[p].on) и требует обновления → снять drawn[p].on.
 * Проход 2 (банк 0x5C): блит видимых, требующих обновления, в (x,y) кадром
 * (sx,sy) → записать drawn[p] = (x,y), on = 1; снять dbit (страница p
 * актуальна для этого спрайта).  z-order = порядок массива.
 *
 * DI живёт внутри leaf'ов (по строке) — скобка _bgi_begin/_bgi_end
 * прерывания НЕ трогает (только мапит W3), поэтому длина прохода не
 * влияет на аудио-ISR.  w,h кадра ≤ 255 (см. sprite.h).
 */
#include "../_bgi.h"
#include <sprite.h>

void sprite_update(sprite_t *arr, uint8_t count)
{
    uint8_t p     = gfx_get_draw_page();               /* 0 / 1        */
    uint8_t dbit  = p ? _SPR_DIRTY1 : _SPR_DIRTY0;
    uint8_t saved = gfx_get_bank();
    uint8_t clip  = _gfx_sprite_clip;   /* раз на кадр: clip- или noclip-ядро */
    uint8_t i;
    sprite_t *s;

    /* Проход 1: heal ВСЕХ старых позиций, требующих обновления. */
    gfx_set_bank(GFX_BANK_NORMAL);
    _bgi_begin();
    for (i = 0, s = arr; i < count; i++, s++) {
        if (s->drawn[p].on && (s->flags & (dbit | SPR_ALWAYS))) {
            if (clip) _gfx_heal_sprite(s->drawn[p].x, s->drawn[p].y, s->w, s->h);
            else      _gfx_heal_sprite_noclip(s->drawn[p].x, s->drawn[p].y, s->w, s->h);
            s->drawn[p].on = 0;
        }
    }
    _bgi_end();

    /* Проход 2: блит ВСЕХ видимых, требующих обновления. */
    gfx_set_bank(GFX_BANK_SPRITE);
    _bgi_begin();
    for (i = 0, s = arr; i < count; i++, s++) {
        if (s->flags & (dbit | SPR_ALWAYS)) {
            if (s->flags & SPR_VISIBLE) {
                if (clip) _gfx_blit_sprite(s->x, s->y, s->img, s->sx, s->sy, s->w, s->h);
                else      _gfx_blit_sprite_noclip(s->x, s->y, s->img, s->sx, s->sy, s->w, s->h);
                s->drawn[p].x  = s->x;
                s->drawn[p].y  = s->y;
                s->drawn[p].on = 1;
            }
            s->flags &= ~dbit;      /* страница p обновлена для спрайта */
        }
    }
    _bgi_end();

    gfx_set_bank(saved);
}
