#!/usr/bin/env python3
"""Render unclassified tile-class gaps as review galleries."""
from __future__ import annotations

import argparse
import json
from collections import defaultdict
from pathlib import Path

from decode_cns import write_png
from render_tileset_usage_gallery import blit_scaled_tile, draw_text
from render_map_preview import load_tileset_rgb


ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "out"


def fill_rect(
    canvas: bytearray,
    width: int,
    x: int,
    y: int,
    rect_w: int,
    rect_h: int,
    color: tuple[int, int, int],
) -> None:
    for yy in range(y, y + rect_h):
        if yy < 0:
            continue
        for xx in range(x, x + rect_w):
            if xx < 0:
                continue
            offset = (yy * width + xx) * 3
            if 0 <= offset <= len(canvas) - 3:
                canvas[offset : offset + 3] = bytes(color)


def draw_border(
    canvas: bytearray,
    width: int,
    x: int,
    y: int,
    rect_w: int,
    rect_h: int,
    color: tuple[int, int, int],
) -> None:
    fill_rect(canvas, width, x, y, rect_w, 1, color)
    fill_rect(canvas, width, x, y + rect_h - 1, rect_w, 1, color)
    fill_rect(canvas, width, x, y, 1, rect_h, color)
    fill_rect(canvas, width, x + rect_w - 1, y, 1, rect_h, color)


def fallback_color(row: dict) -> tuple[int, int, int]:
    fallback_pass = row["fallbackPass"]
    fallback_block = row["fallbackBlock"]
    if fallback_pass and fallback_block:
        return (241, 196, 15)
    if fallback_pass:
        return (46, 204, 113)
    return (231, 76, 60)


def render_tileset_gallery(tileset: str, rows: list[dict], dst: Path) -> None:
    scale = 3
    cell_w = 78
    cell_h = 68
    columns = 8
    height_rows = (len(rows) + columns - 1) // columns
    width = columns * cell_w
    height = max(1, height_rows) * cell_h
    canvas = bytearray((18, 18, 18)) * width * height
    tileset_rgb = load_tileset_rgb(tileset)

    for index, row in enumerate(rows):
        cell_x = (index % columns) * cell_w
        cell_y = (index // columns) * cell_h
        draw_border(canvas, width, cell_x + 3, cell_y + 2, cell_w - 6, cell_h - 5, fallback_color(row))
        blit_scaled_tile(canvas, width, cell_x + 15, cell_y + 5, row["tile"], tileset_rgb, scale)
        draw_text(canvas, width, cell_x + 5, cell_y + 54, str(row["tile"]), (255, 255, 255))
        draw_text(canvas, width, cell_x + 43, cell_y + 54, str(row["count"]), (180, 220, 255))

    dst.parent.mkdir(parents=True, exist_ok=True)
    write_png(dst, width, height, bytes(canvas))


def render_galleries(rows: list[dict], out_dir: Path) -> list[dict]:
    by_tileset: dict[str, list[dict]] = defaultdict(list)
    for row in rows:
        by_tileset[row["tileset"]].append(row)

    rendered = []
    for tileset in sorted(by_tileset):
        dst = out_dir / f"{tileset}_gaps.png"
        render_tileset_gallery(tileset, by_tileset[tileset], dst)
        rendered.append({
            "tileset": tileset,
            "count": len(by_tileset[tileset]),
            "path": dst,
        })
    return rendered


def markdown(rendered: list[dict], out_dir: Path) -> str:
    lines = [
        "# Tile Class Gap Galleries",
        "",
        "Generated from `out/tile_class_gaps.json`.",
        "",
        "Border color shows current fallback behavior: green = fallback pass, red = fallback block, yellow = mixed. "
        "White number is tile ID; blue number is total unclassified uses.",
        "",
    ]
    for item in rendered:
        rel = item["path"].relative_to(out_dir.parent)
        lines.extend([
            f"## {item['tileset']} ({item['count']} gaps)",
            "",
            f"![{item['tileset']} gaps]({rel.as_posix()})",
            "",
        ])
    return "\n".join(lines)


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--gaps", type=Path, default=OUT / "tile_class_gaps.json")
    parser.add_argument("--out-dir", type=Path, default=OUT / "tile_class_gap_galleries")
    parser.add_argument("--index", type=Path, default=OUT / "tile_class_gap_galleries.md")
    args = parser.parse_args()

    rows = json.loads(args.gaps.read_text(encoding="utf-8"))
    rendered = render_galleries(rows, args.out_dir)
    args.index.write_text(markdown(rendered, args.out_dir), encoding="utf-8")
    print(f"wrote {len(rendered)} tile class gap galleries -> {args.index}")


if __name__ == "__main__":
    main()
