#!/usr/bin/env python3
"""
png2abgr2222.py - Convert RGBA PNG(s) to ABGR2222 byte arrays in a C header.

- Rotates each image 90 degrees by default.
- Supports both square and rectangular PNGs.
- Emits one header with arrays + WIDTH/HEIGHT/SIZE macros for each icon.

Usage:
  python png2abgr2222.py --inputs icon.png
  python png2abgr2222.py --inputs icon1.png icon2.png -o icons.h
  python png2abgr2222.py --inputs battery.png -o battery_icon.h --name BATTERY

Install deps:
  pip install pillow
"""
from __future__ import annotations
import argparse
import re
from pathlib import Path
from typing import List, Tuple
from PIL import Image


def sanitize_symbol(s: str) -> str:
    """Turn a filename stem into a valid C identifier (UPPER_CASE)."""
    base = re.sub(r'\W+', '_', s).strip('_')
    if not base:
        base = "ICON"
    if base[0].isdigit():
        base = "_" + base
    return base.upper()


def convert_icon_to_abgr2222(png_path: Path) -> Tuple[bytes, int, int]:
    """Convert RGBA PNG to ABGR2222 (rotate 90 degrees, pack 2 bits per channel).

    Rectangular images are supported. If an image is rotated, width and height
    in the generated header describe the rotated pixel buffer.
    Example: 33x28 input -> 28x33 output after 90-degree rotation.
    """
    img = Image.open(png_path).convert("RGBA")
    w, h = img.width, img.height

    out = bytearray()
    for y in range(h):
        for x in range(w):
            r, g, b, a = img.getpixel((x, y))
            # Keep top 2 bits of each channel, ABGR order in output byte
            a = (a >> 6) & 0x03
            b = (b >> 6) & 0x03
            g = (g >> 6) & 0x03
            r = (r >> 6) & 0x03
            out.append((a << 6) | (b << 4) | (g << 2) | r)
    return bytes(out), w, h


def format_c_array(data: bytes, per_line: int = 16, indent: str = "    ") -> str:
    lines: List[str] = []
    for i in range(0, len(data), per_line):
        chunk = data[i:i+per_line]
        lines.append(indent + ", ".join(f"0x{b:02X}" for b in chunk))
    return ",\n".join(lines)


def write_header(
    out_path: Path,
    entries: List[Tuple[str, Path, bytes, int, int]],
) -> None:
    """
    Write a C header with one array per entry.

    entries: list of (SYMBOL, source_path, data_bytes, width, height)
    """
    guard = sanitize_symbol(out_path.stem) + "_H"
    with out_path.open("w", newline="\n") as f:
        f.write(
            "/*\n"
            " * Auto-generated by png2abgr2222.py\n"
            " * Do not edit by hand.\n"
            " */\n"
            "#pragma once\n"
            "#include <stdint.h>\n\n"
            "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n"
        )
        for sym, src, data, w, h in entries:
            size = len(data)
            f.write(f"/* Source: {src.name} | {w}x{h} | {size} bytes ABGR2222 */\n")
            f.write(f"#define {sym}_WIDTH  {w}\n")
            f.write(f"#define {sym}_HEIGHT {h}\n")
            f.write(f"#define {sym}_SIZE   {size}\n")
            f.write(f"static const uint8_t {sym}_ABGR2222[{size}] = {{\n")
            f.write(format_c_array(data))
            f.write("\n};\n\n")
        f.write("#ifdef __cplusplus\n}\n#endif\n")


def main() -> None:
    ap = argparse.ArgumentParser(description="Convert PNG(s) to ABGR2222 C header.")
    ap.add_argument("--inputs", nargs="+", type=Path, help="Input PNG file(s)")
    ap.add_argument("-o", "--out", type=Path, help="Output header path (default: <first>.h or icons.h)")
    ap.add_argument("--name", help="Base symbol name (only valid with a single input)")
    ap.add_argument("--prefix", default="", help="Optional prefix for generated symbols (e.g., GL_)")
    args = ap.parse_args()

    inputs: List[Path] = args.inputs
    for p in inputs:
        if not p.exists():
            ap.error(f"Input does not exist: {p}")
        if p.suffix.lower() != ".png":
            ap.error(f"Not a PNG: {p}")

    out = args.out
    if out is None:
        out = (inputs[0].with_suffix(".h") if len(inputs) == 1 else Path("icons.h"))

    entries: List[Tuple[str, Path, bytes, int, int]] = []

    if len(inputs) == 1 and args.name:
        base_sym = sanitize_symbol(args.name)
        data, w, h = convert_icon_to_abgr2222(inputs[0])
        sym = (args.prefix + base_sym) if args.prefix else base_sym
        entries.append((sym, inputs[0], data, w, h))
    else:
        for p in inputs:
            base_sym = sanitize_symbol(p.stem)
            sym = (args.prefix + base_sym) if args.prefix else base_sym
            data, w, h = convert_icon_to_abgr2222(p)
            entries.append((sym, p, data, w, h))

    out.parent.mkdir(parents=True, exist_ok=True)
    write_header(out, entries)
    print(f"Wrote {out} ({len(entries)} item{'s' if len(entries)!=1 else ''})")


if __name__ == "__main__":
    main()
