#!/usr/bin/env python3
"""Render CNS map body layout candidates.

The current runtime uses planar_layers: first half layer0, second half layer1.
This helper compares the discarded alternatives without changing the web
prototype:

* planar_layers: current runtime candidate.
* interleaved_layers: adjacent {u16 layer0, u16 layer1} per cell.
* horizontal_pairs: each cell stores two side-by-side 16px tiles.
* vertical_pairs: each cell stores two stacked 16px tiles.
"""
from __future__ import annotations

import argparse
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))

from decode_cns import decompress_cns, write_png
from render_map_preview import draw_layer, load_tileset_rgb


def parse_values(src: Path) -> tuple[int, int, list[int]]:
    decoded = decompress_cns(src.read_bytes())
    if len(decoded) < 4:
        raise ValueError(f"{src} is too short")
    data_offset = 4
    width = int.from_bytes(decoded[0:2], "little")
    height = int.from_bytes(decoded[2:4], "little")
    expected_len = data_offset + width * height * 4
    if len(decoded) != expected_len and len(decoded) >= 2:
        compact_width = decoded[0]
        compact_height = decoded[1]
        compact_expected = 2 + compact_width * compact_height * 4
        if compact_width and compact_height and len(decoded) == compact_expected:
            data_offset = 2
            width = compact_width
            height = compact_height
    values = [
        int.from_bytes(decoded[i : i + 2], "little")
        for i in range(data_offset, len(decoded), 2)
    ]
    expected = width * height * 2
    if len(values) != expected:
        raise ValueError(f"{src}: got {len(values)} u16 values, expected {expected}")
    return width, height, values


def render_layers(
    dst: Path,
    width: int,
    height: int,
    layers: list[list[int]],
    tilesets: list[str],
) -> None:
    info = {
        "width": width,
        "height": height,
        "tileSize": 16,
        "tileIndexOffset": 0,
        "tilesetColumns": 40,
    }
    canvas_width = width * 16
    canvas_height = height * 16
    canvas = bytearray(canvas_width * canvas_height * 3)
    loaded = {name: load_tileset_rgb(name) for name in dict.fromkeys(tilesets)}
    for index, layer in enumerate(layers):
        draw_layer(
            canvas,
            canvas_width,
            info,
            layer,
            loaded[tilesets[min(index, len(tilesets) - 1)]],
            "row40_zero",
        )
    write_png(dst, canvas_width, canvas_height, bytes(canvas))


def candidates(width: int, height: int, values: list[int]) -> dict[str, tuple[int, int, list[list[int]]]]:
    cells = width * height
    return {
        "planar_layers": (width, height, [values[:cells], values[cells:]]),
        "interleaved_layers": (width, height, [values[0::2], values[1::2]]),
        "horizontal_pairs": (width * 2, height, [values]),
        "vertical_pairs": (
            width,
            height * 2,
            [
                [
                    values[(y * width + x) * 2 + half]
                    for y in range(height)
                    for half in range(2)
                    for x in range(width)
                ]
            ],
        ),
    }


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("map", type=Path)
    parser.add_argument("--tilesets", nargs="+", required=True, help="tilesets without .cns")
    parser.add_argument("--out-dir", type=Path, default=Path("out/layout_probes"))
    args = parser.parse_args()

    width, height, values = parse_values(args.map)
    args.out_dir.mkdir(parents=True, exist_ok=True)
    for name, (candidate_width, candidate_height, layers) in candidates(width, height, values).items():
        dst = args.out_dir / f"{args.map.stem}_{name}.png"
        render_layers(dst, candidate_width, candidate_height, layers, args.tilesets)
        print(
            f"{name}: {candidate_width}x{candidate_height} tiles, "
            f"{len(layers)} layer(s) -> {dst}"
        )


if __name__ == "__main__":
    main()
