#!/usr/bin/env python3
"""
room_compose.py — генерик-компоновщик полноэкранного фона комнаты из
тайлов ПРОИЗВОЛЬНОГО размера (см. applications/PoP/docs/PORT_PLAN.md §4:
один полноэкранный фон на комнату готовится офлайн, поверх — спрайты
через sprite.h).  Не завязан ни на какой конкретный проект/игру — просто
берёт раскладку тайлов, которую вы сами опишете, и картинки, которые
сами укажете.

Раскладка — JSON:
  {
    "rows": [
      {"height": 20, "tiles": ["a.png", "b.png", ...]},
      {"height": 60, "tiles": [...]},
      ...
    ]
  }

Тайлы в ряду кладутся слева направо; высота ряда — из "height" (тайл
меньшей высоты — сверху, остаток — прозрачность 0xFF; выше — ошибка).
Ширина итоговой картинки = сумма ширин тайлов первого ряда — остальные
ряды обязаны давать ТУ ЖЕ сумму (иначе ошибка, во избежание кривого
экрана по краю).

Использование:
  room_compose.py OUT.raw OUT.pal --layout layout.json [--base-index N]

Выход:
  OUT.raw — ГОТОВЫЙ формат getimage (uint16 w, uint16 h LE + пиксели
            построчно) — грузится fread'ом целиком и передаётся
            напрямую в gfx_blit(x, y, buf).
  OUT.pal — палитра (EGA 16 + свои цвета), формат gfx_pal_fload.
"""
import argparse
import json
import struct
import sys
from pathlib import Path
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"room_compose: {msg}")


def main():
    ap = argparse.ArgumentParser(add_help=False)
    ap.add_argument("out_raw")
    ap.add_argument("out_pal")
    ap.add_argument("--layout", required=True)
    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")
    args = ap.parse_args()

    spec = json.loads(Path(args.layout).read_text())
    rows_spec = spec["rows"]
    if not rows_spec:
        die("layout: пустой список rows")

    rows_imgs = []
    width = None
    for ri, row in enumerate(rows_spec):
        rh = int(row["height"])
        tiles = row["tiles"]
        if not tiles:
            die(f"row {ri}: пустой список tiles")
        imgs = [Image.open(t).convert("RGBA") for t in tiles]
        for t, im in zip(tiles, imgs):
            if im.height > rh:
                die(f"{t}: высота {im.height} > высоты ряда {rh}")
        row_w = sum(im.width for im in imgs)
        if width is None:
            width = row_w
        elif row_w != width:
            die(f"row {ri}: ширина {row_w} != ширины первого ряда {width} "
                f"(все ряды должны давать одинаковую суммарную ширину)")
        rows_imgs.append((rh, imgs))

    height = sum(rh for rh, _ in rows_imgs)
    if width > 65535 or height > 65535:
        die(f"{width}x{height}: превышает предел формата (u16)")

    colors = {}

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

    canvas = [0xFF] * (width * height)
    y0 = 0
    for rh, imgs in rows_imgs:
        x0 = 0
        for im in imgs:
            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[(y0 + y) * width + (x0 + x)] = color_index(r, g, b)
            x0 += im.width
        y0 += rh

    with open(args.out_raw, "wb") as f:
        f.write(struct.pack("<HH", width, height))
        f.write(bytes(canvas))

    pal = bytearray()
    if args.ega:
        for b, g, r in EGA:
            pal += bytes((b, g, r, 0))
    for (r, g, b) in colors:
        pal += bytes((b, g, r, 0))
    open(args.out_pal, "wb").write(pal)

    print(f"room_compose: {width}x{height} ({len(rows_imgs)} рядов) "
          f"-> {args.out_raw} ({4 + width * height} Б, вкл. getimage-заголовок)")
    print(f"              палитра {len(colors)} своих цветов "
          f"({len(pal)} Б) -> {args.out_pal}")


if __name__ == "__main__":
    main()
