#!/usr/bin/env python3
"""
Generate a C translation unit that embeds every PNG icon under this
directory tree into a single static lookup table.

Usage:  embed_icons.py <icons_dir> <output_c_file>

The emitted file declares the following C-API (see icon_data.h for the
matching public declarations):

    static const unsigned char icon_<name>_png[] = { 0x89, 0x50, ... };
    static const unsigned int  icon_<name>_png_len = N;
    ... one pair per icon ...

    static const struct AMuleIconEntry icons[] = {
        { "amule", icon_amule_png, sizeof icon_amule_png },
        { "flag_us", icon_flag_us_png, sizeof icon_flag_us_png },
        ...
    };

    const struct AMuleIconEntry *amule_find_icon(const char *name);
    const struct AMuleIconEntry *amule_get_all_icons(int *count);

Icons in <icons_dir>/<name>.png get the art-id "<name>".
Icons in <icons_dir>/flags/<code>.png get the art-id "flag_<code>".
"""

import os
import sys
from pathlib import Path


def sanitise(name: str) -> str:
    """Convert an icon name to a C identifier component."""
    return name.replace("-", "_").replace(".", "_")


def collect_icons(icons_dir: Path):
    """
    Yield (art_id, c_ident, file_path) for every .png under icons_dir.

    Files directly under icons_dir use their stem as the art id
    ("foo.png" -> art_id "foo").  Files under icons_dir/flags/ get the
    "flag_" prefix ("us.png" -> art_id "flag_us").
    """
    entries = []

    # Top-level icons (everything except the flags/ subdir).
    for path in sorted(icons_dir.glob("*.png")):
        stem = path.stem
        entries.append((stem, sanitise(stem), path))

    # Country flags.
    flags_dir = icons_dir / "flags"
    if flags_dir.is_dir():
        for path in sorted(flags_dir.glob("*.png")):
            stem = path.stem
            art_id = f"flag_{stem}"
            entries.append((art_id, sanitise(art_id), path))

    return entries


def emit(out_path: Path, entries):
    chunks = []
    chunks.append("/*\n")
    chunks.append(" * Auto-generated by src/icons/embed_icons.py — do not edit.\n")
    chunks.append(" *\n")
    chunks.append(" * Embeds every PNG icon under src/icons/ as a static byte array,\n")
    chunks.append(" * exposes them to C++ via the C-API declared in icon_data.h.\n")
    chunks.append(" */\n")
    chunks.append("\n")
    chunks.append('#include "icon_data.h"\n')
    chunks.append("\n")

    # Per-icon byte arrays.
    for _art_id, c_ident, path in entries:
        data = path.read_bytes()
        chunks.append(f"static const unsigned char icon_{c_ident}_png[] = {{\n")
        # 16 bytes per line, hex-formatted.
        line = []
        for i, b in enumerate(data):
            line.append(f"0x{b:02x}")
            if (i + 1) % 16 == 0:
                chunks.append("\t" + ", ".join(line) + ",\n")
                line = []
        if line:
            chunks.append("\t" + ", ".join(line) + "\n")
        chunks.append("};\n")
        chunks.append("\n")

    # Lookup table.
    chunks.append("static const struct AMuleIconEntry icons[] = {\n")
    for art_id, c_ident, _path in entries:
        chunks.append(
            f'\t{{ "{art_id}", icon_{c_ident}_png, sizeof icon_{c_ident}_png }},\n'
        )
    chunks.append("};\n")
    chunks.append("\n")
    chunks.append(
        "static const int icons_count = (int) (sizeof icons / sizeof icons[0]);\n"
    )
    chunks.append("\n")

    # API.
    chunks.append("#include <string.h>\n")
    chunks.append("\n")
    chunks.append(
        "const struct AMuleIconEntry *amule_find_icon(const char *name)\n"
    )
    chunks.append("{\n")
    chunks.append("\tint i;\n")
    chunks.append("\tfor (i = 0; i < icons_count; ++i) {\n")
    chunks.append("\t\tif (strcmp(icons[i].name, name) == 0) {\n")
    chunks.append("\t\t\treturn &icons[i];\n")
    chunks.append("\t\t}\n")
    chunks.append("\t}\n")
    chunks.append("\treturn (const struct AMuleIconEntry *) 0;\n")
    chunks.append("}\n")
    chunks.append("\n")
    chunks.append(
        "const struct AMuleIconEntry *amule_get_all_icons(int *count)\n"
    )
    chunks.append("{\n")
    chunks.append("\tif (count) {\n")
    chunks.append("\t\t*count = icons_count;\n")
    chunks.append("\t}\n")
    chunks.append("\treturn icons;\n")
    chunks.append("}\n")

    out_path.parent.mkdir(parents=True, exist_ok=True)
    out_path.write_text("".join(chunks))


def main(argv):
    if len(argv) != 3:
        print(__doc__, file=sys.stderr)
        return 2

    icons_dir = Path(argv[1]).resolve()
    out_path = Path(argv[2]).resolve()

    if not icons_dir.is_dir():
        print(f"error: {icons_dir} is not a directory", file=sys.stderr)
        return 1

    entries = collect_icons(icons_dir)
    if not entries:
        print(f"error: no .png files found under {icons_dir}", file=sys.stderr)
        return 1

    emit(out_path, entries)
    print(f"emitted {len(entries)} icons -> {out_path}")
    return 0


if __name__ == "__main__":
    sys.exit(main(sys.argv))
