/*
 * pop_kid.c — движок Kid (Фаза K1): порт seg006.c play_seq/load_frame +
 * seqtbl-интерпретатор.  Данные — kid_data.h.
 */
#include <graphics.h>
#include <gfx.h>
#include <sprite.h>
#include "pop_kid.h"
#include "kid_data.h"

kid_t Kid;

/* Координатные таблицы (data.h). */
static const uint8_t x_bump[20] = {
    (uint8_t)-12, 2, 16, 30, 44, 58, 72, 86, 100, 114,
    128, 142, 156, 170, 184, 198, 212, 226, 240, 254
};
static const int16_t y_land[5] = {-8, 55, 118, 181, 244};
#define FIRST_ONSCREEN_COLUMN 5
#define TILE_SIZEX 14

/* ---- Атласы Kid (прямая адресация image>>3) ------------------------ */
#define KID_MAXPAGES 28
static atlas_t kidp[KID_MAXPAGES];
static uint8_t kid_npages;

int pop_kid_load(uint8_t npages)
{
    static const char *const names[KID_MAXPAGES] = {
        "kid0.atl","kid1.atl","kid2.atl","kid3.atl","kid4.atl","kid5.atl",
        "kid6.atl","kid7.atl","kid8.atl","kid9.atl","kid10.atl","kid11.atl",
        "kid12.atl","kid13.atl","kid14.atl","kid15.atl","kid16.atl","kid17.atl",
        "kid18.atl","kid19.atl","kid20.atl","kid21.atl","kid22.atl","kid23.atl",
        "kid24.atl","kid25.atl","kid26.atl","kid27.atl"
    };
    uint8_t i;
    if (npages > KID_MAXPAGES) npages = KID_MAXPAGES;
    for (i = 0; i < npages; i++)
        if (atlas_load(&kidp[i], names[i]) != 0)
            return -1;
    kid_npages = npages;
    return 0;
}

void pop_kid_free(void)
{
    uint8_t i;
    for (i = 0; i < kid_npages; i++)
        atlas_free(&kidp[i]);
    kid_npages = 0;
}

/* ---- seqtbl-доступ (база SEQTBL_BASE) ------------------------------ */
#define SEQ(cs) kid_seqtbl[(uint16_t)((cs) - SEQTBL_BASE)]

static uint8_t char_dx_forward(int8_t dx)
{
    if (Kid.direction < 0) dx = (int8_t)-dx;
    return (uint8_t)(Kid.x + dx);
}

/* Аксессоры к текущему кадру (для pop_map — без дублирования kid_frames). */
int8_t  kid_cur_dx(void)    { return kid_frames[Kid.frame].dx; }
uint8_t kid_cur_flags(void) { return kid_frames[Kid.frame].flags; }

/* seqtbl_offset_char: сменить последовательность Kid (единая точка —
 * pop_ctrl/pop_map зовут ЕЁ, а не дублируют kid_seq_off/kid_seqtbl). */
void kid_set_seq(uint8_t seq_id) { Kid.curr_seq = kid_seq_off[seq_id]; }

/* play_seq (seg006.c:570): крутит опкоды до КАДРА (байт < 0xF0). */
void play_seq(void)
{
    for (;;) {
        uint8_t cmd = SEQ(Kid.curr_seq);
        Kid.curr_seq++;
        switch (cmd) {
        case 0xFB: /* DX */
            Kid.x = char_dx_forward((int8_t)SEQ(Kid.curr_seq));
            Kid.curr_seq++;
            break;
        case 0xFA: /* DY */
            Kid.y = (uint8_t)(Kid.y + (int8_t)SEQ(Kid.curr_seq));
            Kid.curr_seq++;
            break;
        case 0xFE: /* FLIP */
            Kid.direction = (int8_t)~Kid.direction;
            break;
        case 0xF7: /* JMP_IF_FEATHER: нет пера -> пропустить адрес */
            Kid.curr_seq += 2;
            break;
        case 0xFF: { /* JMP */
            uint16_t a = (uint16_t)(SEQ(Kid.curr_seq) |
                         ((uint16_t)SEQ((uint16_t)(Kid.curr_seq + 1)) << 8));
            Kid.curr_seq = a;
            break;
        }
        case 0xFD: /* UP */
            Kid.curr_row--;
            break;
        case 0xFC: /* DOWN */
            Kid.curr_row++;
            break;
        case 0xF9: /* ACTION */
            Kid.action = SEQ(Kid.curr_seq);
            Kid.curr_seq++;
            break;
        case 0xF8: /* SET_FALL */
            Kid.fall_x = (int8_t)SEQ(Kid.curr_seq); Kid.curr_seq++;
            Kid.fall_y = (int8_t)SEQ(Kid.curr_seq); Kid.curr_seq++;
            break;
        case 0xF5: case 0xF4: /* KNOCK up/down */
            break;
        case 0xF2: /* SOUND — потребить 1 байт */
            Kid.curr_seq++;
            break;
        case 0xF1: /* END_LEVEL */
            break;
        case 0xF3: /* GET_ITEM — потребить 1 байт */
            Kid.curr_seq++;
            break;
        case 0xF6: /* DIE nop */
            break;
        default: /* КАДР */
            Kid.frame = cmd;
            return;
        }
    }
}

void kid_init(uint8_t seq_id, int8_t col, int8_t row, int8_t dir)
{
    Kid.curr_col = col;
    Kid.curr_row = row;
    Kid.direction = dir;
    Kid.x = (uint8_t)(x_bump[col + FIRST_ONSCREEN_COLUMN] + TILE_SIZEX);
    Kid.y = (uint8_t)y_land[row + 1];
    Kid.fall_x = Kid.fall_y = 0;
    Kid.repeat = 0;
    Kid.curr_seq = kid_seq_off[seq_id];
    play_seq();                 /* до первого кадра */
}

void kid_tick(void)
{
    play_seq();
}

/* Отрисовка текущего кадра: obj_x=(char_x+dx)*2-116, obj_y=char_y+dy
 * (seg008.c:1728 load_frame_to_obj).  НИЗ спрайта на obj_y, левый край
 * на obj_x.  Калибровка XOFF/YOFF — сверить с эталоном.
 *
 * Зеркалирование (K2a): спрайты Kid хранятся ЛИЦОМ ВЛЕВО (как в оригинале).
 * seg008.c:864 — флип, когда персонаж смотрит ВПРАВО (direction==dir_0_right
 * ==0), и тогда xpos -= image->w (draw_mid).  Реализуем бесплатным
 * горизонтальным флипом column-major спрайта (gfx_blit_cols flip=1):
 * зеркало происходит ВНУТРИ [x,x+w), поэтому при флипе подаём x=obj_x-w,
 * чтобы «hot-point» кадра остался на месте.
 *
 * Kid рисуется банком GFX_BANK_SPRITE (только видео-ОЗУ, НЕ теневая копия
 * — 0xFF прозрачен), чтобы kid_heal() мог восстановить чистый фон из
 * теневой копии (фон запёкся в неё при pop_room_draw).  Прямоугольник
 * последней отрисовки запоминается для heal. */
#define KID_XOFF 0
#define KID_YOFF 0

/* Прямоугольник последней отрисовки Kid — ПО СТРАНИЦЕ (0/1).  У каждой
 * графической страницы своя ОЗУ-копия/видео-ОЗУ, поэтому heal должен
 * стирать пиксели Kid именно той страницы, в которую сейчас рисуем
 * (при дабл-буфере страница чередуется — прошлый Kid на этой странице
 * был 2 логических кадра назад).  В однобуфере используется только [0]. */
static int kid_lx[2], kid_ly[2];
static uint16_t kid_lw[2], kid_lh[2];
static uint8_t kid_lvalid[2];

/* Метрики последнего kid_draw для футпринта fore-over-Kid (pop_bg):
 * obj_x — ЛОГИЧЕСКАЯ X кадра (ДО ×8/7), obj_y — низ, w/h — размер. */
static int kid_fp_x, kid_fp_y;
static uint16_t kid_fp_w, kid_fp_h;
int      kid_fp_obj_x(void)  { return kid_fp_x; }
int      kid_fp_obj_y(void)  { return kid_fp_y; }
uint16_t kid_fp_width(void)  { return kid_fp_w; }
uint16_t kid_fp_height(void) { return kid_fp_h; }

/* Восстановить фон из теневой копии на месте ПРЕДЫДУЩЕГО кадра Kid. */
void kid_heal(void)
{
    uint8_t p = gfx_get_draw_page();
    if (kid_lvalid[p])
        gfx_heal(kid_lx[p], kid_ly[p], kid_lw[p], kid_lh[p]);
    kid_lvalid[p] = 0;
}

void kid_draw(void)
{
    kframe fr = kid_frames[Kid.frame];
    int fwd, obj_x, obj_y, top;
    uint8_t page, idx;
    const uint8_t *img;
    uint16_t w, h;

    if (fr.image == 255)
        return;                 /* пустой кадр */
    fwd = (int)Kid.x + ((Kid.direction < 0) ? -fr.dx : fr.dx);
    obj_x = (fwd << 1) - 116;
    obj_y = (int)Kid.y + fr.dy;
    if ((int8_t)(fr.flags ^ Kid.direction) >= 0)
        obj_x++;                /* even/odd pixel (в ЛОГИЧЕСКОЙ координате) */
    kid_fp_x = obj_x;           /* ЛОГИЧЕСКАЯ X (до ×8/7) — для футпринта fore */
    kid_fp_y = obj_y;
    /* seg008 calc_screen_x_coord: логическая X (280 шир = 28px/тайл) ->
     * экран (320 шир = 32px/тайл), ×320/280 = ×8/7.  БЕЗ этого Kid уезжает
     * влево от тайлов тем сильнее, чем правее (col9 ~36px).  Коллизия — в
     * логической системе (не масштабируется). */
    obj_x = obj_x * 8 / 7;

    page = (uint8_t)(fr.image >> 3);
    idx = (uint8_t)(fr.image & 7);
    if (page >= kid_npages)
        return;                 /* страница не загружена */
    img = (const uint8_t *)atlas_image(&kidp[page], idx);
    gfx_w0_map(kidp[page].page);
    w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
    h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
    if (w && h) {
        uint8_t flip = (Kid.direction >= 0);   /* dir_0_right -> зеркало */
        int bx = obj_x + KID_XOFF;
        if (flip)
            bx -= (int)w;                       /* seg008: xpos -= image->w */
        top = obj_y - (int)h + 1 + KID_YOFF;
        uint8_t dp = gfx_get_draw_page();       /* в какую страницу рисуем */
        gfx_set_bank(GFX_BANK_SPRITE);   /* видео-ОЗУ, без теневой копии */
        gfx_blit_cols(bx, top, img, flip);
        gfx_set_bank(GFX_BANK_NORMAL);
        kid_lx[dp] = bx; kid_ly[dp] = top;
        kid_lw[dp] = w; kid_lh[dp] = h; kid_lvalid[dp] = 1;
        kid_fp_w = w; kid_fp_h = h;      /* размер кадра для футпринта fore */
    }
    gfx_w0_unmap();
}
