/*
 * gfx_scroll_h — ГОРИЗОНТАЛЬНЫЙ скролл региона (mode-agnostic обёртка).
 *
 * Берёт чистый фон из НЕактивной (теневой) страницы и копирует его со
 * сдвигом dx по X в активную (draw) страницу.  Обе стороны — банк 0x50
 * (GFX_BANK_NORMAL): запись бьёт VRAM + ОЗУ-копию, т.е. копия
 * ОДНОВРЕМЕННО скроллит И «лечит» целевую страницу (старые спрайты
 * затираются сдвинутым фоном; scroll-impl-guide.md §2).
 *
 * dx > 0 — сдвиг в сторону УВЕЛИЧЕНИЯ X (изображение едет вправо,
 * открывается полоса слева); dx < 0 — влево (открывается справа);
 * dx == 0 — чистая копия фона (полный heal целевой страницы).  В обеих
 * страницах остаёмся в пределах area.  Открывшуюся |dx|-полосу НЕ
 * заполняем — возвращаем её в *dirty (её дозаполняет вызывающий).
 *
 * РЕАЛИЗАЦИЯ — построчно, через _bgi_scroll_rows_raw (без страйдов, один
 * OUT Port_Y на строку — на ~5-7% быстрее колоночного _bgi_scroll_cols_raw,
 * которому на колонку нужен OUT и на чтение, и на запись).  Горизонтальный
 * сдвиг — чистая разница CPU-адресов src/dst (src_x vs dst_x), Port_Y для
 * read и write один (одна строка).  DI/EI бандами по 16 строк — ВНУТРИ
 * leaf'а; он же режет высоту на банды и принимает h 1..256 (0=256), так что
 * здесь делим только ширину >256 (лимит accel-блока).  Перекрытий src/dst
 * нет: стороны в разных страницах.
 */

#include "../include/sprite.h"
#include "../_bgi.h"

void gfx_scroll_h(const gfx_rect_t *area, int16_t dx, gfx_rect_t *dirty) {

    uint8_t  saved = gfx_get_bank();
    uint16_t adx   = (dx < 0) ? (uint16_t)(-dx) : (uint16_t)dx;
    /* src-строка стартует правее на |dx| при сдвиге влево, dst — правее
     * на dx при сдвиге вправо; так оба края остаются внутри area. */
    uint16_t src_x  = area->x + ((dx < 0) ? adx : 0);
    uint16_t dst_x  = area->x + ((dx > 0) ? adx : 0);
    uint16_t w      = area->w - adx;         /* байт (пикселей) в строке */
    uint8_t  h      = (uint8_t)area->h;       /* строк (256 => 0 = 256 у leaf) */
    uint16_t src = _gfx_addr_shadow_base + src_x;
    uint16_t dst = _gfx_addr_base + dst_x;

    gfx_set_bank(GFX_BANK_NORMAL);
    _bgi_begin();

    /* Ширина >256 байт — банды ≤256 (размер accel-блока, 0=256).  Высоту и
     * DI/EI-банды по 16 строк leaf делает сам (h: 0=256).  Между бандами по
     * ширине CPU-адрес двигаем на 256 (Port_Y выбирает строку). */
    while (w) {
        // Закомментированный блок должен работать правильно но не работает
        // почему-то при w > 256 второй проход мусорит
        //   TODO: разобраться 
        // uint8_t cw = (w > 256) ? 256 : (uint8_t)w;   /* 0 => 256 */
        // _bgi_scroll_rows_raw(src, dst, cw, h, (uint8_t)area->y);
        // src += 256; dst += 256;
        // w -= (w > 256) ? 256 : w;
        uint8_t cw = (w > 255) ? 255 : (uint8_t)w;   /* 0 => 256 */
        _bgi_scroll_rows_raw(src, dst, cw, h, (uint8_t)area->y);
        src += cw; dst += cw; w -= cw;
    }

    _bgi_end();
    gfx_set_bank(saved);

    if (dirty) {
        dirty->x = (dx >= 0) ? area->x : (int16_t)(area->x + area->w - adx);
        dirty->y = area->y;
        dirty->w = adx;
        dirty->h = area->h;
    }
}
