#!/usr/bin/env python3
"""Regenerate every kat5200 application-icon asset from a single high-res master.

Authoring source of truth lives in this directory:
    Fuji.png               - the rainbow Atari Fuji (blocky, retro pixels)
    SfAtarianSystem.ttf    - the font used for the in-app "KAT5200" title
                             (extracted from src/gui/font_sf_atarian_system.h)

The script builds a 1024x1024 master (master.png) and derives all platform
assets from it.  It is idempotent: re-run any time the design changes and every
downstream icon is rebuilt.  Run from anywhere:

    python3 resources/icon/make_icons.py

Requires Pillow.  Pure-Python (no ImageMagick) so it runs the same everywhere.

Design (locked):
  * Panel  : rounded square in neutral near-black grey #0F0F0F
             (nk_rgb(15,15,15)).  Transparent outside.
  * Fuji   : Fuji.png upscaled NEAREST (keep blocky pixels), black keyed out.
  * K / T  : SF Atarian System, blue #4287DC (nk_rgb(66,135,220),
             g_theme.alternate) - same blue as the in-app title.
"""

import os
from PIL import Image, ImageDraw, ImageFont

HERE = os.path.dirname(os.path.abspath(__file__))
RES = os.path.dirname(HERE)                       # resources/
ROOT = os.path.dirname(RES)                       # project root
ANDROID_RES = os.path.join(ROOT, "android-project", "app", "src", "main", "res")

PANEL = (15, 15, 15, 255)      # #0F0F0F  neutral near-black dark grey
BLUE = (66, 135, 220, 255)     # #4287DC  g_theme.alternate
FONT = os.path.join(HERE, "SfAtarianSystem.ttf")
FUJI = os.path.join(HERE, "Fuji.png")

S = 1024                       # master edge


# ---------------------------------------------------------------------------
# building blocks
# ---------------------------------------------------------------------------
def load_fuji():
    """Fuji.png with its black background keyed to transparent (alpha kept)."""
    im = Image.open(FUJI).convert("RGBA")
    px = im.load()
    w, h = im.size
    for y in range(h):
        for x in range(w):
            r, g, b, a = px[x, y]
            if r < 24 and g < 24 and b < 24:        # near-black -> transparent
                px[x, y] = (r, g, b, 0)
    return im


def fuji_scaled(target_h):
    """Upscale the Fuji to target_h with NEAREST so the retro pixels stay crisp."""
    im = load_fuji()
    w, h = im.size
    tw = round(w * target_h / h)
    return im.resize((tw, target_h), Image.NEAREST)


def draw_mark(size, scale, bg=True):
    """Render the K + Fuji + T mark on a `size` square canvas.

    scale  - Fuji height as a fraction of the canvas (tight ~0.74, safe ~0.47).
             NOTE: the mark is wider than it is tall (the K and T flank the
             Fuji), so the limiting dimension for the adaptive-icon safe zone is
             *width*, not height.  At scale 0.47 the mark spans ~59% of the
             canvas, leaving >=20% margin each side so it stays inside the
             central 66dp/108dp safe-zone circle and no launcher mask (circle,
             squircle, rounded square) clips the K or T.
    bg     - paint the rounded panel (True for icons, False for adaptive fg).
    """
    img = Image.new("RGBA", (size, size), (0, 0, 0, 0))
    d = ImageDraw.Draw(img)

    if bg:
        radius = round(size * 0.176)               # ~iOS/Android squircle feel
        d.rounded_rectangle([0, 0, size - 1, size - 1], radius=radius, fill=PANEL)

    cx = size / 2.0
    cy = size / 2.0

    # Fuji: centred, height = scale of canvas.
    fuji_h = round(size * scale)
    fuji = fuji_scaled(fuji_h)
    fx = round(cx - fuji.width / 2)
    fy = round(cy - fuji.height / 2)
    img.alpha_composite(fuji, (fx, fy))

    # K and T flank the Fuji with their tops just below the Fuji's top, so they
    # sit beside the narrow upper bars and clear the spreading legs (no overlap).
    font = ImageFont.truetype(FONT, round(size * scale * 0.86))
    letter_top = fy + size * scale * 0.06           # just below the Fuji top
    gap = fuji.width * 0.40                          # horizontal offset from centre
    _draw_top(d, font, "K", cx - gap, letter_top)
    _draw_top(d, font, "T", cx + gap, letter_top)
    return img


def _draw_top(d, font, ch, cx, top):
    """Draw `ch` horizontally centred on cx with its visual top at `top`."""
    l, t, r, b = d.textbbox((0, 0), ch, font=font)
    d.text((cx - (l + r) / 2, top - t), ch, font=font, fill=BLUE)


# ---------------------------------------------------------------------------
# output helpers
# ---------------------------------------------------------------------------
def save_png(img, *parts):
    path = os.path.join(*parts)
    os.makedirs(os.path.dirname(path), exist_ok=True)
    img.save(path)
    print("  ", os.path.relpath(path, ROOT), img.size)


def resized(master, n, resample=Image.LANCZOS):
    return master.resize((n, n), resample)


# ---------------------------------------------------------------------------
# the banner (Android TV): "KAT5200" wordmark with the Fuji trailing it at the
# same height, baseline-aligned, on the panel colour.
# ---------------------------------------------------------------------------
def make_banner(w, h):
    img = Image.new("RGBA", (w, h), PANEL)
    d = ImageDraw.Draw(img)
    text = "KAT5200"

    # Pick the largest font that fits both height and width: wordmark + a gap +
    # the Fuji (scaled to the wordmark's cap height) inside the safe margins.
    pad_x = round(w * 0.05)
    avail_w = w - 2 * pad_x
    max_h = round(h * 0.62)
    fs = max_h * 2
    while fs > 8:
        font = ImageFont.truetype(FONT, fs)
        l, t, r, b = d.textbbox((0, 0), text, font=font)
        text_w, text_h = r - l, b - t
        gap = round(text_h * 0.30)
        fuji_w = round(text_h * 450 / 377)          # Fuji at cap height
        if text_h <= max_h and (text_w + gap + fuji_w) <= avail_w:
            break
        fs -= 2

    fuji = fuji_scaled(text_h)
    total_w = text_w + gap + fuji.width
    x0 = round((w - total_w) / 2)                    # centre the group
    baseline = round((h + text_h) / 2)               # shared bottom edge

    d.text((x0 - l, baseline - text_h - t), text, font=font, fill=BLUE)
    img.alpha_composite(fuji, (x0 + text_w + gap, baseline - fuji.height))
    return img


# memoise the two master compositions
_tight = None
_safe = None


def make_mark_master_tight():
    global _tight
    if _tight is None:
        _tight = draw_mark(S, 0.74, bg=True)
    return _tight


def make_mark_master_safe():
    global _safe
    if _safe is None:
        _safe = draw_mark(S, 0.47, bg=False)       # foreground only, safe zone
    return _safe


# ---------------------------------------------------------------------------
# main
# ---------------------------------------------------------------------------
def main():
    print("master + shared png:")
    master = make_mark_master_tight()
    save_png(master, HERE, "master.png")
    save_png(master, RES, "kat5200.png")           # macOS .icns source + shared

    print("windows .ico:")
    ico_sizes = [16, 32, 48, 64, 128, 256]
    master.save(os.path.join(RES, "kat5200.ico"),
                sizes=[(n, n) for n in ico_sizes])
    print("   resources/kat5200.ico", ico_sizes)

    print("runtime window icon .bmp (256, 32-bit w/ alpha):")
    resized(master, 256).save(os.path.join(RES, "kat5200.bmp"))
    print("   resources/kat5200.bmp")

    print("linux hicolor pngs:")
    for n in (256, 128, 64, 48):
        save_png(resized(master, n), HERE, "hicolor", f"{n}x{n}.png")

    print("android legacy mipmaps (ic_launcher.png):")
    for bucket, n in (("mdpi", 48), ("hdpi", 72), ("xhdpi", 96),
                      ("xxhdpi", 144), ("xxxhdpi", 192)):
        save_png(resized(master, n), ANDROID_RES, f"mipmap-{bucket}",
                 "ic_launcher.png")

    print("android adaptive foreground (safe-zone, no panel):")
    safe = make_mark_master_safe()
    # adaptive layers are full 108dp; content sits in the centred safe zone.
    for bucket, n in (("mdpi", 108), ("hdpi", 162), ("xhdpi", 216),
                      ("xxhdpi", 324), ("xxxhdpi", 432)):
        save_png(resized(safe, n), ANDROID_RES, f"mipmap-{bucket}",
                 "ic_launcher_foreground.png")

    print("android tv banner (multi-density):")
    for bucket, (bw, bh) in (("mdpi", (160, 90)), ("hdpi", (240, 135)),
                             ("xhdpi", (320, 180)), ("xxhdpi", (480, 270))):
        save_png(make_banner(bw, bh), ANDROID_RES, f"drawable-{bucket}",
                 "banner.png")

    print("done.")


if __name__ == "__main__":
    main()
