#!/usr/bin/env python3
"""
png_strip.py — генерик-конвертер произвольного набора PNG-кадров (РАЗНОГО
размера каждый) в одну raw-ленту + палитру для sprite-движка Sprinter
(формат getimage/atlas — см. libbgi/include/sprite.h, toolchain/mkatlas.py).

Не завязан ни на какой конкретный проект/игру — просто берёт список
картинок, которые вы сами укажете, и паковает их в ленту с общим
канвасом (кадр движка обязан быть фиксированного размера).

Использование:
  png_strip.py OUT.raw OUT.pal img1.png img2.png ... [опции]

Опции:
  --canvas WxH       размер канваса кадра (по умолчанию — максимум
                     ширины/высоты среди входных картинок)
  --anchor MODE      bottom-center (по умолчанию) | top-left | center
  --base-index N     с какого индекса палитры начинать свои цвета
                     (по умолчанию 16 — под 16 EGA-слотов initgraph)
  --ega              добавить в палитру 16 EGA-цветов BGI перед своими
                     (по умолчанию включено; --no-ega выключает)
  --seed-pal FILE    переиспользовать цвета уже существующей палитры
                     (EGA16 + N своих) — свои индексы этой ленты
                     продолжаются ПОСЛЕ них (не с --base-index), а
                     OUT.pal получится ПОЛНОЙ merged-палитрой
                     (ega+seed+свои), готовой как --seed-pal для
                     следующего вызова.  Так несколько независимо
                     собранных лент (напр. разные типы тайлов) можно
                     свести в ОДНУ палитру для одновременного показа
                     на экране (см. applications/PoP/poc/tools/
                     build_room_palette.py).

Каждая картинка — источник ОДНОГО кадра ленты (не атлас-сетка;
для сетки — см. mkatlas.py raw:FWxFH:NXxNY после этого шага, если
нужно). Прозрачность — альфа < 128 → индекс 0xFF (сентинел движка).

Дальше: toolchain/mkatlas.py OUT.atl OUT.raw:CW x CH:1xN
"""
import argparse
import sys
from PIL import Image

EGA = [
    (0, 0, 0), (168, 0, 0), (0, 168, 0), (168, 168, 0),
    (0, 0, 168), (168, 0, 168), (0, 84, 168), (168, 168, 168),
    (84, 84, 84), (255, 84, 84), (84, 255, 84), (255, 255, 84),
    (84, 84, 255), (255, 84, 255), (84, 255, 255), (255, 255, 255),
]


def die(msg):
    sys.exit(f"png_strip: {msg}")


def main():
    ap = argparse.ArgumentParser(add_help=False)
    ap.add_argument("out_raw")
    ap.add_argument("out_pal")
    ap.add_argument("images", nargs="+")
    ap.add_argument("--canvas", default=None)
    ap.add_argument("--anchor", default="bottom-center",
                     choices=["bottom-center", "top-left", "center"])
    ap.add_argument("--base-index", type=int, default=16)
    ap.add_argument("--ega", dest="ega", action="store_true", default=True)
    ap.add_argument("--no-ega", dest="ega", action="store_false")
    ap.add_argument("--seed-pal", default=None)
    args = ap.parse_args()

    imgs = [Image.open(p).convert("RGBA") for p in args.images]

    seed_records = []
    if args.seed_pal:
        raw = open(args.seed_pal, "rb").read()
        if len(raw) % 4:
            die(f"--seed-pal {args.seed_pal}: длина не кратна 4")
        n_ega = 16 if args.ega else 0
        for i in range(n_ega, len(raw) // 4):
            b, g, r, _ = raw[i * 4:i * 4 + 4]
            seed_records.append((r, g, b))
        args.base_index = 16 + len(seed_records)

    if args.canvas:
        cw, ch = (int(v) for v in args.canvas.lower().split("x"))
    else:
        cw = max(im.width for im in imgs)
        ch = max(im.height for im in imgs)
    if cw > 64 or ch > 64:
        die(f"канвас {cw}x{ch} превышает лимит движка 64x64")
    for p, im in zip(args.images, imgs):
        if im.width > cw or im.height > ch:
            die(f"{p}: {im.width}x{im.height} не влезает в канвас {cw}x{ch}")

    colors = {}
    for i, rgb in enumerate(seed_records):
        colors[rgb] = 16 + i

    def color_index(r, g, b):
        key = (r, g, b)
        if key not in colors:
            idx = args.base_index + (len(colors) - len(seed_records))
            if idx >= 0xFF:
                die("палитра переполнена (>= 0xFF своих цветов)")
            colors[key] = idx
        return colors[key]

    data = bytearray()
    for im in imgs:
        if args.anchor == "top-left":
            ox, oy = 0, 0
        elif args.anchor == "center":
            ox, oy = (cw - im.width) // 2, (ch - im.height) // 2
        else:  # bottom-center
            ox, oy = (cw - im.width) // 2, ch - im.height
        canvas = [0xFF] * (cw * ch)
        for y in range(im.height):
            for x in range(im.width):
                r, g, b, a = im.getpixel((x, y))
                if a < 128:
                    continue
                canvas[(oy + y) * cw + (ox + x)] = color_index(r, g, b)
        data.extend(canvas)
    open(args.out_raw, "wb").write(data)

    pal = bytearray()
    if args.ega:
        for b, g, r in EGA:
            pal += bytes((b, g, r, 0))
    new_colors = [rgb for rgb in colors if rgb not in seed_records]
    for (r, g, b) in (*seed_records, *new_colors):
        pal += bytes((b, g, r, 0))
    open(args.out_pal, "wb").write(pal)

    print(f"png_strip: {len(imgs)} кадров, канвас {cw}x{ch}, "
          f"{len(data)} Б -> {args.out_raw}")
    print(f"           палитра {len(seed_records)} seed + {len(new_colors)} новых "
          f"своих цветов ({len(pal)} Б) -> {args.out_pal}")
    print(f"дальше: mkatlas.py OUT.atl {args.out_raw}:{cw}x{ch}:1x{len(imgs)}")


if __name__ == "__main__":
    main()
