"""
prepare_win_gfx.py

Convert Doom intermission label PNGs (win_gfx/) to VB 2bpp tile + map arrays.
Each image is 12px tall (2 tile rows, padded), variable width.

Output: src/vbdoom/assets/images/win_gfx.c and win_gfx.h
"""

import os
from PIL import Image

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
INPUT_DIR = os.path.join(SCRIPT_DIR, "win_gfx")
OUTPUT_C = os.path.join(SCRIPT_DIR, "src", "vbdoom", "assets", "images", "win_gfx.c")
OUTPUT_H = os.path.join(SCRIPT_DIR, "src", "vbdoom", "assets", "images", "win_gfx.h")

TILE_W = 8
TILE_H = 8

# Images to convert: (filename, C prefix)
LABELS = [
    ("WITIME.png",   "witime"),
    ("WISCRT2.png",  "wiscrt"),
    ("WIOSTK.png",   "wiostk"),
    ("WIOSTI.png",   "wiosti"),
    ("WIENTER.png",  "wienter"),
    ("WIF.png",      "wif"),
    ("WIFRGS.png",   "wifrgs"),
    ("WIMSTT.png",   "wimstt"),
    ("WIPCNT.png",   "wipcnt"),
]


def build_palette_remap(img):
    """Map PNG palette indices to VB brightness levels (0-3)."""
    pal = img.getpalette()
    if pal is None:
        # Not indexed -- convert from RGB
        return None

    used_indices = sorted(set(img.getdata()))
    idx_brightness = []
    for idx in used_indices:
        r = pal[idx * 3]
        idx_brightness.append((idx, r))

    idx_brightness.sort(key=lambda x: x[1])
    remap = {}
    for vb_level, (png_idx, _) in enumerate(idx_brightness):
        if vb_level > 3:
            vb_level = 3
        remap[png_idx] = vb_level

    return remap


def pixel_to_vb(img, px, py, remap):
    """Get VB brightness for pixel at (px, py)."""
    if px >= img.width or py >= img.height:
        return 0  # padding
    val = img.getpixel((px, py))
    if remap is not None:
        return remap.get(val, 0)
    # RGB mode
    if isinstance(val, tuple):
        r = val[0]
        if r < 32:
            return 0
        elif r < 96:
            return 1
        elif r < 192:
            return 2
        else:
            return 3
    return val & 3


def encode_tile_words(pixels):
    """Convert 64-pixel list to 4 VB 2bpp u32 words (sequential format)."""
    words = []
    for pair in range(4):
        row_a = pair * 2
        row_b = pair * 2 + 1
        u16_a = 0
        u16_b = 0
        for x in range(TILE_W):
            u16_a |= (pixels[row_a * TILE_W + x] << (x * 2))
            u16_b |= (pixels[row_b * TILE_W + x] << (x * 2))
        word = u16_a | (u16_b << 16)
        words.append(word)
    return words


def convert_image(filepath, prefix):
    """Convert one label PNG to tile data + map entries."""
    img = Image.open(filepath)
    w, h = img.size

    # Pad height to multiple of 8 (tiles are 8x8)
    tile_cols = (w + TILE_W - 1) // TILE_W
    tile_rows = (h + TILE_H - 1) // TILE_H

    remap = None
    if img.mode == 'P':
        remap = build_palette_remap(img)
    elif img.mode == 'RGBA':
        img = img.convert('RGB')

    # Build tile bank with dedup
    tile_bank = []      # list of word-tuples
    tile_lookup = {}    # pixel_tuple -> index
    map_entries = []    # one u16 per cell

    # Tile 0 = blank
    blank = tuple([0] * 64)
    blank_words = tuple(encode_tile_words(list(blank)))
    tile_bank.append(blank_words)
    tile_lookup[blank] = 0

    for ty in range(tile_rows):
        for tx in range(tile_cols):
            pixels = []
            for py in range(TILE_H):
                for px in range(TILE_W):
                    pixels.append(pixel_to_vb(img, tx * TILE_W + px, ty * TILE_H + py, remap))
            pixels = tuple(pixels)

            if pixels in tile_lookup:
                map_entries.append(tile_lookup[pixels])
            else:
                idx = len(tile_bank)
                tile_bank.append(tuple(encode_tile_words(list(pixels))))
                tile_lookup[pixels] = idx
                map_entries.append(idx)

    print(f"  {os.path.basename(filepath)}: {w}x{h} -> {tile_cols}x{tile_rows} tiles, "
          f"{len(tile_bank)} unique, {len(tile_bank)*16} bytes")

    return tile_bank, map_entries, tile_cols, tile_rows


def main():
    print("Converting win_gfx label images to VB 2bpp tile data...\n")

    all_results = []
    for fname, prefix in LABELS:
        fpath = os.path.join(INPUT_DIR, fname)
        if not os.path.isfile(fpath):
            print(f"  WARNING: {fpath} not found, skipping")
            continue
        result = convert_image(fpath, prefix)
        all_results.append((prefix, result))

    # Write C file
    with open(OUTPUT_C, 'w') as f:
        f.write("/* win_gfx -- Doom intermission label tiles\n")
        f.write(" * Auto-generated by prepare_win_gfx.py\n */\n\n")

        for prefix, (tile_bank, map_entries, tcols, trows) in all_results:
            num_words = len(tile_bank) * 4
            all_words = [w for tile in tile_bank for w in tile]

            f.write(f"/* {prefix}: {tcols}x{trows} tiles, {len(tile_bank)} unique */\n")
            f.write(f"const unsigned int {prefix}Tiles[{num_words}]"
                    f" __attribute__((aligned(4))) =\n{{\n")
            for i, word in enumerate(all_words):
                if i % 8 == 0:
                    f.write("\t")
                f.write(f"0x{word:08X}")
                if i < num_words - 1:
                    f.write(",")
                if i % 8 == 7:
                    f.write("\n")
            if num_words % 8 != 0:
                f.write("\n")
            f.write("};\n\n")

            f.write(f"const unsigned short {prefix}Map[{len(map_entries)}]"
                    f" __attribute__((aligned(4))) =\n{{\n")
            for i, entry in enumerate(map_entries):
                if i % 8 == 0:
                    f.write("\t")
                f.write(f"0x{entry:04X}")
                if i < len(map_entries) - 1:
                    f.write(",")
                if i % 8 == 7:
                    f.write("\n")
            if len(map_entries) % 8 != 0:
                f.write("\n")
            f.write("};\n\n")

    # Write H file
    with open(OUTPUT_H, 'w') as f:
        f.write("#ifndef __WIN_GFX_H__\n#define __WIN_GFX_H__\n\n")
        for prefix, (tile_bank, map_entries, tcols, trows) in all_results:
            num_words = len(tile_bank) * 4
            f.write(f"#define {prefix.upper()}_TILES  {len(tile_bank)}\n")
            f.write(f"#define {prefix.upper()}_TILE_BYTES  {len(tile_bank)*16}\n")
            f.write(f"#define {prefix.upper()}_COLS  {tcols}\n")
            f.write(f"#define {prefix.upper()}_ROWS  {trows}\n")
            f.write(f"extern const unsigned int {prefix}Tiles[{num_words}];\n")
            f.write(f"extern const unsigned short {prefix}Map[{len(map_entries)}];\n\n")
        f.write("#endif\n")

    print(f"\nWritten: {OUTPUT_C}")
    print(f"Written: {OUTPUT_H}")


if __name__ == "__main__":
    main()
