#!/usr/bin/env python3
# Generate the on-screen bitmap assets the CircuitPython firmware (app.py) blits:
#   logo.bin  - the VARASYS wordmark (no tagline), tinted brand cyan at the top
#   midi.bin  - a 5-pin DIN icon (lights green when a MIDI host is listening)
#   usb.bin   - the USB "trident" icon (lights when the device is USB-connected)
#
# Each file is a single 4-bit-alpha image with a 2-byte header (matches the font packing
# in gen_font.py, just without the glyph metrics table):
#     byte 0 = width, byte 1 = height, then ((w*h+1)//2) bytes of 4-bit alpha,
#     row-major, two pixels per byte (first pixel = high nibble).
# app.py's load_alpha()/make_glyph() decode it and blend bg->fg per pixel (smooth).
#
# Re-run after changing the logo/icons:  python3 pico-cp/gen_assets.py
# Writes pico-cp/{logo,midi,usb}.bin and /tmp/assets_verify.png (eyeball it).

import math, pathlib
from PIL import Image, ImageDraw

HERE = pathlib.Path(__file__).parent
LOGO_PNG = pathlib.Path.home() / "src/varasys_logo/For using with dark colored background/VARASYS Limited.png"
SS = 6   # supersample factor for the drawn icons (downscaled -> anti-aliased)


def pack(coverage_img):
    """coverage_img: 'L' image (0..255 alpha). -> bytes(w, h, packed 4-bit alpha)."""
    w, h = coverage_img.size
    assert w <= 255 and h <= 255, "asset too large for the 1-byte dims (%dx%d)" % (w, h)
    px = coverage_img.load()
    nib = []
    for y in range(h):
        for x in range(w):
            nib.append(px[x, y] >> 4)            # 8-bit -> 4-bit alpha
    if len(nib) % 2:
        nib.append(0)
    out = bytearray([w, h])
    for i in range(0, len(nib), 2):
        out.append((nib[i] << 4) | nib[i + 1])
    return bytes(out)


def make_logo(target_w=156):
    img = Image.open(LOGO_PNG).convert("RGBA")
    r, g, b, a = img.split()
    # Coverage = alpha if the PNG is genuinely transparent, else brightness (cyan on a flat bg).
    if a.getextrema()[0] < 250:
        cov = a
    else:
        cov = img.convert("L")
    bbox = cov.getbbox()
    if bbox:
        cov = cov.crop(bbox)
    w, h = cov.size
    th = max(1, round(target_w * h / w))
    cov = cov.resize((target_w, th), Image.LANCZOS)
    return pack(cov)


def make_midi(size=22):
    cw = size * SS
    img = Image.new("L", (cw, cw), 0)
    d = ImageDraw.Draw(img)
    cx = cy = cw / 2
    R = cw * 0.44
    lw = max(2, int(cw * 0.07))
    d.ellipse([cx - R, cy - R, cx + R, cy + R], outline=255, width=lw)   # connector shell
    # 5 pins in the DIN fan (one top-centre, a flanking pair above, a wider pair below)
    ring = R * 0.60
    pr = cw * 0.075
    pins = [(0.0, -1.0), (-0.72, -0.55), (0.72, -0.55), (-0.95, 0.18), (0.95, 0.18)]
    for fx, fy in pins:
        x = cx + fx * ring
        y = cy + fy * ring
        d.ellipse([x - pr, y - pr, x + pr, y + pr], fill=255)
    return pack(img.resize((size, size), Image.LANCZOS))


def make_usb(size=22):
    cw = size * SS
    img = Image.new("L", (cw, cw), 0)
    d = ImageDraw.Draw(img)
    cx = cw / 2
    lw = max(2, int(cw * 0.075))
    top, bot = cw * 0.10, cw * 0.90
    d.line([(cx, top + cw * 0.10), (cx, bot)], fill=255, width=lw)        # shaft
    # arrowhead at the top
    ah = cw * 0.13
    d.polygon([(cx, top), (cx - ah, top + ah * 1.4), (cx + ah, top + ah * 1.4)], fill=255)
    # base plug (filled circle at the bottom)
    br = cw * 0.10
    d.ellipse([cx - br, bot - br, cx + br, bot + br], fill=255)
    # left branch -> small filled circle
    ly = cw * 0.46
    lx = cx - cw * 0.26
    d.line([(cx, ly + cw * 0.10), (lx, ly)], fill=255, width=lw)
    cr = cw * 0.085
    d.ellipse([lx - cr, ly - cr, lx + cr, ly + cr], fill=255)
    # right branch -> small filled square
    ry = cw * 0.34
    rx = cx + cw * 0.26
    d.line([(cx, ry + cw * 0.10), (rx, ry)], fill=255, width=lw)
    sq = cw * 0.08
    d.rectangle([rx - sq, ry - sq, rx + sq, ry + sq], fill=255)
    return pack(img.resize((size, size), Image.LANCZOS))


def unpack_to_img(blob, fg=(10, 179, 247), bg=(6, 9, 14)):
    """Decode like app.py would, for the verify sheet."""
    w, h = blob[0], blob[1]
    im = Image.new("RGB", (w, h), bg)
    for k in range(w * h):
        byte = blob[2 + (k >> 1)]
        nib = (byte >> 4) if (k & 1) == 0 else (byte & 0xF)
        t = nib * 17
        col = tuple((bg[i] * (255 - t) + fg[i] * t) // 255 for i in range(3))
        im.putpixel((k % w, k // w), col)
    return im


def main():
    assets = {"logo": make_logo(), "midi": make_midi(), "usb": make_usb()}
    for name, blob in assets.items():
        (HERE / (name + ".bin")).write_bytes(blob)
        print("wrote %s.bin  %dx%d  %d bytes" % (name, blob[0], blob[1], len(blob)))
    # verify sheet on a dark panel-like background
    pad = 12
    imgs = [unpack_to_img(b) for b in assets.values()]
    W = max(i.width for i in imgs) + pad * 2
    H = sum(i.height for i in imgs) + pad * (len(imgs) + 1)
    sheet = Image.new("RGB", (W, H), (6, 9, 14))
    y = pad
    for i in imgs:
        sheet.paste(i, (pad, y))
        y += i.height + pad
    sheet.save("/tmp/assets_verify.png")
    print("verify -> /tmp/assets_verify.png")


if __name__ == "__main__":
    main()
