/*
 * pop_bg.c — статический фон комнаты PoP композицией слоёв (порт
 * applications/PoP/toolchain/render_room.py -> seg008.c draw_tile/
 * wall_pattern).  Подробности алгоритма — в render_room.py и
 * memory/pop_background_strategy.
 *
 * Только DUNGEON-стиль (Фаза 1).  Анимация (пламя факелов, шипы, дверь-
 * плита, сама решётка ворот) НЕ рисуется — отдельными sprite_t поверх
 * (Фаза 2).  Решётка ворот из ЛЕВОЙ комнаты рисуется статичным кадром.
 */
#include <graphics.h>
#include <gfx.h>
#include <sprite.h>
#include <fcntl.h>
#include <unistd.h>
#include "pop_bg.h"

/* ---- Атласы (прямая адресация) ------------------------------------- */
static atlas_t env[5];
static atlas_t wall_at;
static atlas_t fore_at;

static const char *const ENV_ATL[5] = {
    "pop_env0.atl", "pop_env1.atl", "pop_env2.atl",
    "pop_env3.atl", "pop_env4.atl"
};

int pop_bg_load(void)
{
    int i;
    for (i = 0; i < 5; i++)
        if (atlas_load(&env[i], ENV_ATL[i]) != 0) return -1;
    if (atlas_load(&wall_at, "pop_wall.atl") != 0) return -1;
    if (atlas_load(&fore_at, "pop_fore.atl") != 0) return -1;
    return 0;
}

void pop_bg_free(void)
{
    int i;
    for (i = 0; i < 5; i++)
        atlas_free(&env[i]);
    atlas_free(&wall_at);
    atlas_free(&fore_at);
}

/* Блит ленты idx атласа a: НИЗ спрайта на ybottom, левый край x,
 * прозрачно (0xFF).  w/h читаются из getimage-заголовка ленты (в W0). */
static void blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
{
    const uint8_t *img;
    uint16_t w, h;
    if (idx >= a->count)
        return;
    img = (const uint8_t *)atlas_image(a, idx);
    gfx_w0_map(a->page);
    w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
    h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
    if (w && h)
        gfx_blit(x, ybottom - (int)h + 1, img);
    gfx_w0_unmap();
}

static void env_b(uint8_t id, int x, int yb)  { blit_b(&env[id >> 5], (uint8_t)(id & 31), x, yb); }
static void wall_b(uint8_t id, int x, int yb) { blit_b(&wall_at, id, x, yb); }
static void fore_b(uint8_t id, int x, int yb) { blit_b(&fore_at, id, x, yb); }

/* ---- Таблица тайлов (piece, seg008.c:27) --------------------------- */
typedef struct {
    uint8_t base_id, floor_left;  int8_t base_y;
    uint8_t right_id, floor_right; int8_t right_y;
    uint8_t stripe_id, topright_id, bottom_id, fore_id, fore_x; int8_t fore_y;
} piece;

static const piece tile_table[31] = {
    {  0, 0,  0,   0, 0, 0,   0,  0,  0,  0, 0,  0}, /* 00 empty */
    { 41, 1,  0,  42, 1, 2, 145,  0, 43,  0, 0,  0}, /* 01 floor */
    {127, 1,  0, 133, 1, 2, 145,  0, 43,  0, 0,  0}, /* 02 spike */
    { 92, 1,  0,  93, 1, 2,   0, 94, 43, 95, 1,  0}, /* 03 pillar */
    { 46, 1,  0,  47, 1, 2,   0, 48, 43, 49, 3,  0}, /* 04 gate */
    { 41, 1,  1,  35, 1, 3, 145,  0, 36,  0, 0,  0}, /* 05 stuck floor */
    { 41, 1,  0,  42, 1, 2, 145,  0, 96,  0, 0,  0}, /* 06 close button */
    { 46, 1,  0,   0, 0, 2,   0,  0, 43, 49, 3,  0}, /* 07 door top w/floor */
    { 86, 1,  0,  87, 1, 2,   0,  0, 43, 88, 1,  0}, /* 08 big pillar bottom */
    {  0, 0,  0,  89, 0, 3,   0, 90,  0, 91, 1,  3}, /* 09 big pillar top */
    { 41, 1,  0,  42, 1, 2, 145,  0, 43, 12, 2, -3}, /* 0A potion */
    {  0, 1,  0,   0, 0, 0, 145,  0,  0,  0, 0,  0}, /* 0B loose floor */
    {  0, 0,  0,   0, 0, 2,   0,  0, 85, 49, 3,  0}, /* 0C door top */
    { 75, 1,  0,  42, 1, 2,   0,  0, 43, 77, 0,  0}, /* 0D mirror */
    { 97, 1,  0,  98, 1, 2, 145,  0, 43,100, 0,  0}, /* 0E debris */
    {147, 1,  0,  42, 1, 1, 145,  0,149,  0, 0,  0}, /* 0F open button */
    { 41, 1,  0,  37, 0, 0,   0, 38, 43,  0, 0,  0}, /* 10 leveldoor left */
    {  0, 0,  0,  39, 1, 2,   0, 40, 43,  0, 0,  0}, /* 11 leveldoor right */
    {  0, 0,  0,  42, 1, 2, 145,  0, 43,  0, 0,  0}, /* 12 chomper */
    { 41, 1,  0,  42, 1, 2,   0,  0, 43,  0, 0,  0}, /* 13 torch */
    {  0, 0,  0,   1, 1, 2,   0,  2,  0,  0, 0,  0}, /* 14 wall */
    { 30, 1,  0,  31, 1, 2,   0,  0, 43,  0, 0,  0}, /* 15 skeleton */
    { 41, 1,  0,  42, 1, 2, 145,  0, 43,  0, 0,  0}, /* 16 sword */
    { 41, 1,  0,  10, 0, 0,   0, 11, 43,  0, 0,  0}, /* 17 balcony left */
    {  0, 0,  0,  12, 1, 2,   0, 13, 43,  0, 0,  0}, /* 18 balcony right */
    { 92, 1,  0,  42, 1, 2, 145,  0, 43, 95, 1,  0}, /* 19 lattice pillar */
    {  1, 0,  0,   0, 0, 0,   0,  0,  2,  9, 0,-53}, /* 1A lattice down */
    {  3, 0,-10,   0, 0, 0,   0,  0,  0,  9, 0,-53}, /* 1B lattice small */
    {  4, 0,-10,   0, 0, 0,   0,  0,  0,  9, 0,-53}, /* 1C lattice left */
    {  5, 0,-10,   0, 0, 0,   0,  0,  0,  9, 0,-53}, /* 1D lattice right */
    { 97, 1,  0,  98, 1, 2,   0,  0, 43,100, 0,  0}, /* 1E debris w/torch */
};

/* ---- Константные таблицы (render_room.py) -------------------------- */
static const uint8_t COL_XH[10]         = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36};
static const uint8_t TBL_LINE[3]        = {0, 10, 20};
static const uint8_t WALL_FRAM_BOTTOM[4] = {7, 9, 5, 3};
static const uint8_t WALL_FRAM_MAIN[4]   = {8, 10, 6, 4};
static const uint8_t DOOR_FRAM_SLICE[9]  = {67, 59, 58, 57, 56, 55, 54, 53, 52};
static const uint8_t SPIKES_FRAM_RIGHT[10] = {0, 134, 135, 136, 137, 138, 137, 135, 134, 0};
static const uint8_t BLUELINE_FRAM1[4]  = {0, 124, 125, 126};
static const int8_t  BLUELINE_FRAM_Y[4] = {0, -20, -20, 0};
static const uint8_t BLUELINE_FRAM3[4]  = {44, 44, 45, 45};
static const uint8_t LPOS[5]            = {58, 41, 37, 20, 16};
static const uint8_t RPOS[4]            = {52, 42, 31, 21};

/* Wall (chtab_7) id: делители/блок/марки. */
#define RES_WALL_RNDBLOCK 13
#define RES_WALL_DIVIDER1 11
#define RES_WALL_MARK_TL  14
#define RES_WALL_MARK_BL  15
#define RES_WALL_MARK_TR  16
#define RES_WALL_MARK_BR  17

/* ---- Текущая комната (globals для tile_code/mod) ------------------- */
static const uint8_t *g_fg, *g_bg, *g_lfg, *g_lbg;
static uint8_t g_room;

static uint8_t tile_code(int row, int col)
{
    if (col < 0) {
        if (g_lfg && row >= 0 && row < 3) return g_lfg[row];
        return 20;                    /* уровневая кромка = стена */
    }
    if (row < 0) return 1;            /* верхняя кромка = пол */
    if (row > 2 || col > 9) return 20;
    return g_fg[row * 10 + col];
}

static uint8_t tile_mod(int row, int col)
{
    if (col < 0) {
        if (g_lbg && row >= 0 && row < 3) return g_lbg[row];
        return 0;
    }
    if (row < 0 || row > 2 || col > 9) return 0;
    return g_bg[row * 10 + col];
}

/* seg008.c:1255 (без FAKE_TILES): сосед — стена, если его тип == 20.
 * На кромке col0 сосед по умолчанию = стена (как render_room). */
static uint8_t wall_modifier(int row, int col)
{
    uint8_t lw = (col > 0) ? (tile_code(row, col - 1) == 20) : 1;
    uint8_t rw = (col < 9) ? (tile_code(row, col + 1) == 20) : 1;
    if (lw && rw) return 3;           /* WWW */
    if (lw) return 2;                 /* WWS */
    if (rw) return 1;                 /* SWW */
    return 0;                         /* SWS */
}

/* ---- PRNG (seg009.c:321, 32-бит LCG) — только на ВХОД в комнату ---- */
static unsigned long rnd_seed;

static uint16_t prandom(uint16_t maxv)
{
    rnd_seed = rnd_seed * 214013UL + 2531011UL;
    return (uint16_t)((uint16_t)(rnd_seed >> 16) % (uint16_t)(maxv + 1));
}

/* ---- Декали-марки стен (seg008.c:2041/2056) ------------------------ */
static void draw_left_mark(uint16_t dv, int arg2, int arg1, int row, int col)
{
    uint8_t image_id = (dv & 1) ? RES_WALL_MARK_BL : RES_WALL_MARK_TL;
    int lv2 = 0;
    int xh = COL_XH[col];
    int dby = 63 * row + 65;
    if (dv > 3) lv2 = arg1 + 6;
    else if (dv > 1) lv2 = arg2 + 6;
    wall_b(image_id, (xh + ((dv == 2 || dv == 3) ? 1 : 0)) * 8 + lv2, dby - LPOS[dv]);
}

static void draw_right_mark(uint16_t dv, int arg1, int row, int col)
{
    uint8_t image_id = (dv & 1) ? RES_WALL_MARK_BR : RES_WALL_MARK_TR;
    int a = (dv < 2) ? 24 : arg1 - 3;
    int xh = COL_XH[col];
    int dby = 63 * row + 65;
    wall_b(image_id, (xh + ((dv > 1) ? 1 : 0)) * 8 + a, dby - RPOS[dv]);
}

/* ---- wall_pattern (seg008.c:1928, dungeon-ветка) ------------------- */
static void wall_pattern(int row, int col, int which_part)
{
    int xh = COL_XH[col];
    int dby = 63 * row + 65;
    unsigned long saved = rnd_seed;
    uint16_t mid_div, mid_off, bot_div, bot_off;
    uint8_t bg;

    rnd_seed = (unsigned long)(g_room + TBL_LINE[row] + col);
    prandom(1);                       /* сброс */
    mid_div = prandom(1);
    mid_off = prandom(4);
    bot_div = prandom(1);
    bot_off = prandom(4);
    bg = wall_modifier(row, col);

    if (bg == 3) {                    /* WWW */
        if (which_part) {
            if (prandom(4) == 0)
                wall_b(RES_WALL_RNDBLOCK, xh * 8, dby - 42);
            wall_b((uint8_t)(RES_WALL_DIVIDER1 + mid_div), (xh + 1) * 8 + mid_off, dby - 21);
        }
        wall_b((uint8_t)(RES_WALL_DIVIDER1 + bot_div), xh * 8 + bot_off, dby);
        if (which_part) {
            if (prandom(4) == 0)
                draw_right_mark(prandom(3), mid_off, row, col);
            if (prandom(4) == 0)
                draw_left_mark(prandom(4), mid_off - mid_div, bot_off - bot_div, row, col);
        }
    } else if (bg == 0) {             /* SWS */
        if (which_part) {
            if (prandom(6) == 0)
                draw_left_mark(prandom(1), mid_off - mid_div, bot_off - bot_div, row, col);
        }
    } else if (bg == 1) {            /* SWW */
        if (which_part) {
            if (prandom(4) == 0)
                wall_b(RES_WALL_RNDBLOCK, xh * 8, dby - 42);
            wall_b((uint8_t)(RES_WALL_DIVIDER1 + mid_div), (xh + 1) * 8 + mid_off, dby - 21);
            if (prandom(4) == 0)
                draw_right_mark(prandom(3), mid_off, row, col);
            if (prandom(4) == 0)
                draw_left_mark(prandom(3), mid_off - mid_div, bot_off - bot_div, row, col);
        }
    } else {                          /* WWS (bg==2) */
        if (which_part)
            wall_b((uint8_t)(RES_WALL_DIVIDER1 + mid_div), (xh + 1) * 8 + mid_off, dby - 21);
        wall_b((uint8_t)(RES_WALL_DIVIDER1 + bot_div), xh * 8 + bot_off, dby);
        if (which_part) {
            if (prandom(4) == 0)
                draw_right_mark((uint16_t)(prandom(1) + 2), mid_off, row, col);
            if (prandom(4) == 0)
                draw_left_mark(prandom(4), mid_off - mid_div, bot_off - bot_div, row, col);
        }
    }
    rnd_seed = saved;
}

/* topright тайла снизу-слева (seg008.c:410). */
static void draw_topright(uint8_t tt, int x, int dby)
{
    if (tt == 7 || tt == 12) return;
    if (tt == 20) { wall_b(2, x, dby); return; }
    if (tile_table[tt].topright_id)
        env_b(tile_table[tt].topright_id, x, dby);
}

/* Решётка ворот (portcullis) из ЛЕВОЙ комнаты (seg008.c:1110). */
static void draw_gate_back(uint8_t modl, int xh, int dby, int dmy)
{
    int x = xh * 8;
    int gate_top_y = dby - 62;
    int openness = (((modl > 188) ? 188 : modl) >> 2) + 1;
    int gate_bot_y = dmy - openness;
    int yb, gate_frame;

    if (gate_bot_y + 12 < dmy)
        env_b(50, x, gate_bot_y);
    else
        env_b(51, x, gate_bot_y - 2);
    yb = gate_bot_y - 12;
    if (yb < 192) {
        while (yb >= 0 && yb > 7 && (yb - 7) > gate_top_y) {
            env_b(52, x, yb);
            yb -= 8;
        }
    }
    gate_frame = yb - gate_top_y + 1;
    if (gate_frame > 0 && gate_frame < 9)
        env_b(DOOR_FRAM_SLICE[gate_frame], x, yb);
}

/* ---- draw_tile (seg008.c:155) — одна клетка ------------------------ */
static void draw_tile(int row, int col)
{
    uint8_t code = tile_code(row, col);
    uint8_t lcode = tile_code(row, col - 1);
    uint8_t lmod = tile_mod(row, col - 1);
    int xh = COL_XH[col];
    int x = xh * 8;
    int dby = 63 * row + 65;
    int dmy = dby - 3;
    const piece *t = &tile_table[code];
    const piece *lt = &tile_table[lcode];
    uint8_t rbl_code;
    uint8_t wmod;

    /* тайл снизу-слева (для topright) */
    if (row + 1 > 2) rbl_code = 1;              /* ниже нижнего ряда = пол */
    else rbl_code = tile_code(row + 1, col - 1);

    /* --- floorright (seg008.c:392) — только can_see_bottomleft --- */
    if (code == 0 || code == 9 || code == 12 || code == 26) {
        draw_topright(rbl_code, x, dby);
        /* floor-right тень (blitters_9_black) на ЧЁРНОМ фоне = no-op
         * (чёрный силуэт по чёрному) — не рисуем.  Если tile_left имеет
         * floor_right: lt->floor_right. */
    }

    /* --- draw_tile_right (seg008.c:456) --- */
    if (code != 20) {
        if (lcode == 20) {
            wall_b(1, x, tile_table[20].right_y + dmy);        /* грань стены */
        } else if (lcode == 0) {
            if (lmod <= 3 && BLUELINE_FRAM1[lmod])
                env_b(BLUELINE_FRAM1[lmod], x, BLUELINE_FRAM_Y[lmod] + dmy);
        } else if (lcode == 1) {
            uint8_t num;
            env_b(42, x, tile_table[1].right_y + dmy);          /* правый треуг. пола */
            num = (lmod <= 3) ? lmod : 0;
            if (num != 0)                                        /* dungeon: level_type=0 */
                env_b(BLUELINE_FRAM3[num], x, dmy - 20);        /* силуэт кладки */
        } else {
            if (lt->right_id)
                env_b(lt->right_id, x, lt->right_y + dmy);
            /* stripe — только палас, dungeon пропускает */
        }
    }

    /* --- draw_tile_anim_right (seg008.c:530) — статичные части --- */
    if (lcode == 4) {                                           /* gate: решётка */
        draw_gate_back(lmod, xh, dby, dmy);
    } else if (lcode == 11) {                                   /* loose: правый край */
        env_b(42, x, dby - 1);
    } else if (lcode == 2) {                                    /* spike: статич. кадр */
        uint8_t sf = (lmod & 0x80) ? 5 : lmod;
        if (sf < 10 && SPIKES_FRAM_RIGHT[sf])
            env_b(SPIKES_FRAM_RIGHT[sf], x, dmy - 7);
    }

    /* --- draw_tile_bottom (seg008.c:570) --- */
    if (code == 20) {
        wmod = wall_modifier(row, col);
        wall_b(WALL_FRAM_BOTTOM[wmod], x, dby);                 /* сплошная нижняя грань */
        wall_pattern(row, col, 0);
    } else if (t->bottom_id) {
        env_b(t->bottom_id, x, dby);
    }

    /* --- draw_loose (seg008.c:600) — статич. кадр --- */
    if (code == 11)
        env_b(43, x, dby);                                     /* loose_fram_bottom[0] */

    /* --- draw_tile_base (seg008.c:611) --- */
    {
        uint8_t base_id = t->base_id;
        if (code == 11) base_id = 41;                          /* loose_fram_left[0] */
        if (base_id)
            env_b(base_id, x, t->base_y + dmy);
    }

    /* --- draw_tile_fore (seg008.c:690) --- */
    if (code == 20) {
        wmod = wall_modifier(row, col);
        wall_b(WALL_FRAM_MAIN[wmod], x, dmy);                  /* основная грань */
        wall_pattern(row, col, 1);
    }
    if (t->fore_id)
        fore_b(t->fore_id, t->fore_x * 8 + x, t->fore_y + dmy);
}

void pop_room_draw(uint8_t room_num,
                   const uint8_t *fg, const uint8_t *bg,
                   const uint8_t *lcol_fg, const uint8_t *lcol_bg)
{
    int row, col;
    g_room = room_num;
    g_fg = fg; g_bg = bg;
    g_lfg = lcol_fg; g_lbg = lcol_bg;

    /* прозрачный блит: 0xFF не пишется (чёрный фон/нижние слои проступают) */
    gfx_set_bank(GFX_BANK_TRANSPARENT);
    for (row = 2; row >= 0; row--)        /* снизу вверх (2,1,0) */
        for (col = 0; col < 10; col++)
            draw_tile(row, col);
    gfx_set_bank(GFX_BANK_NORMAL);
}

/* ---- fore-over-Kid (порт seg003 redraw_at_char + seg008 set_char_collision) --
 * Передний слой (fore_id = foretable-кусок) тайлов ФУТПРИНТА спрайта Kid
 * — рядов top..bottom × колонок left..right (<= 2×2 = 4 тайла) — рисуется
 * ПОВЕРХ Kid: то, что по изометрии перед ним (передние грани колонн/ворот/
 * большой колонны/щебня).  Стены и факелы имеют fore_id=0 → остаются сзади.
 * Рисуем в GFX_BANK_SPRITE (видео-ОЗУ, 0xFF прозрачен); kid_heal восстановит
 * из теневого фона (fore в нём уже запечён pop_room_draw).  obj_x — ЛОГИЧЕСКАЯ
 * X кадра Kid (до ×8/7), obj_y — низ спрайта, w/h — размер, dir — направление. */
static int8_t col_from_x(int xpos)         /* get_tile_div_mod: колонка (58/14) */
{
    int x = xpos - 58;
    int xh = x / 14;
    if (x % 14 < 0) xh--;                  /* округление вниз для отрицательных */
    return (int8_t)xh;
}

static int8_t y_to_row(int y)              /* y_to_row_mod4 (TILE_SIZEY=63) */
{
    return (int8_t)((y + 60) / 63 % 4 - 1);
}

static void fore_tile(int row, int col)    /* передний слой одного тайла */
{
    uint8_t code = tile_code(row, col);
    const piece *t = &tile_table[code];
    int x = COL_XH[col] * 8;
    int dmy = 63 * row + 62;
    if (code == 20) {                       /* стена: основная грань (foretable, */
        uint8_t wmod = wall_modifier(row, col);  /* draw_tile_fore seg008:690) — */
        wall_b(WALL_FRAM_MAIN[wmod], x, dmy);    /* перекрывает Kid как передний */
        wall_pattern(row, col, 1);
    } else if (t->fore_id) {
        fore_b(t->fore_id, t->fore_x * 8 + x, t->fore_y + dmy);
    }
}

/* Пол-оверлей при подъёме (draw_floor_overlay, seg008:1457): когда Kid
 * подтягивается на floor-подобный тайл назначения (floor/pillar/stuck/torch),
 * а тайл СЛЕВА пуст (т.е. кромка), передний край пола перекрывает нижнюю
 * часть Kid — иначе видна часть, что должна быть за полом.  Кадр подъёма
 * 137..144 индексирует floor_left_overlay. */
static const uint8_t FLOOR_LEFT_OVERLAY[8] = {32, 151, 151, 150, 150, 151, 32, 32};

static void climb_overlay_tile(int row, int col, uint8_t fidx)
{
    uint8_t code = tile_code(row, col);
    int x, dmy, dby;
    const piece *t;
    if (code != 1 && code != 3 && code != 5 && code != 19) return;  /* не floor-тайл */
    if (tile_code(row, col - 1) != 0) return;   /* слева не пусто → не кромка */
    x = COL_XH[col] * 8;
    dmy = 63 * row + 62;
    dby = dmy + 3;
    t = &tile_table[code];
    env_b(FLOOR_LEFT_OVERLAY[fidx], x, dmy + (code == 5 ? 1 : 0));
    if (t->bottom_id) env_b(t->bottom_id, x, dby);   /* draw_tile_bottom */
}

void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
                       int8_t dir, uint8_t frame)
{
    int wh = (int)((w + 1) >> 1);          /* char_width_half */
    int xl = obj_x / 2 + 58;               /* char_x_left (58-база) */
    int xr;
    int8_t cL, cR, rT, rB, r, c;
    if (dir >= 0) xl -= wh;                /* лицом вправо: левый край внутрь */
    xr = xl + wh;
    cL = col_from_x(xl); if (cL < 0) cL = 0; else if (cL > 9) cL = 9;
    cR = col_from_x(xr); if (cR < 0) cR = 0; else if (cR > 9) cR = 9;
    rB = y_to_row(obj_y);               if (rB < 0) rB = 0; else if (rB > 2) rB = 2;
    rT = y_to_row(obj_y - (int)h + 1);  if (rT < 0) rT = 0; else if (rT > 2) rT = 2;
    gfx_set_bank(GFX_BANK_SPRITE);         /* видео-ОЗУ, heal восстановит */
    for (r = rT; r <= rB; r++)
        for (c = cL; c <= cR; c++)
            fore_tile(r, c);
    if (frame >= 137 && frame <= 144) {    /* подъём: пол назначения поверх Kid */
        uint8_t fidx = (uint8_t)(frame - 137);
        for (r = rT; r <= rB; r++)
            for (c = cL; c <= cR; c++)
                climb_overlay_tile(r, c, fidx);
    }
    gfx_set_bank(GFX_BANK_NORMAL);
}
