"""
Generates app.ico + app_256.png for MirrorOverlay.

Design: dark rounded-square badge (the overlay) with a dimmer rounded rectangle
behind / upper-left (the source display being mirrored) and a glowing
accent-blue rounded rectangle in front / lower-right (the mirrored image),
plus a diagonal glass streak.

No third party deps: SDF rendering + a tiny PNG writer + ICO packer.
"""
import math
import struct
import zlib
import os

OUT_DIR = os.path.dirname(os.path.abspath(__file__))

# ---------------------------------------------------------------- helpers
def clamp(v, a=0.0, b=1.0):
    return a if v < a else (b if v > b else v)


def rr_sdf(x, y, cx, cy, w, h, r):
    """Signed distance to a rounded rectangle (<0 inside), in the same units as x/y."""
    dx = abs(x - cx) - (w * 0.5 - r)
    dy = abs(y - cy) - (h * 0.5 - r)
    ox = dx if dx > 0 else 0.0
    oy = dy if dy > 0 else 0.0
    return math.sqrt(ox * ox + oy * oy) + min(max(dx, dy), 0.0) - r


def lerp(a, b, t):
    return a + (b - a) * t


# ---------------------------------------------------------------- render
def render(size):
    ss = 4 if size <= 64 else 3          # supersampling factor
    N = size * ss
    S = float(N)
    aa = ss * 0.65                        # ~0.65 device px AA band

    # size hinting tiers:  tiny <=20px  |  mid 21-24px  |  full detail >=24px
    tiny = size <= 20
    mid = 21 <= size <= 24
    detail = size >= 24

    def pk(a, b, c):
        return a if tiny else (b if mid else c)

    buf = [0.0] * (N * N * 4)

    def cover(d):
        return clamp(0.5 - d / aa)

    def fill(sdf, colfn, soft=1.0):
        band = aa * soft
        for y in range(N):
            yy = y + 0.5
            row = y * N * 4
            for x in range(N):
                d = sdf(x + 0.5, yy)
                if d > band:
                    continue
                c = clamp(0.5 - d / band)
                r, g, b, a = colfn(x + 0.5, yy)
                c *= a
                if c <= 0.0:
                    continue
                i = row + x * 4
                ia = 1.0 - c
                buf[i] = buf[i] * ia + r * c
                buf[i + 1] = buf[i + 1] * ia + g * c
                buf[i + 2] = buf[i + 2] * ia + b * c
                buf[i + 3] = buf[i + 3] * ia + c

    # --- badge (rounded square) -------------------------------------------
    bm = pk(0.026, 0.042, 0.030) * S
    bs = S - 2.0 * bm
    br = pk(0.170, 0.200, 0.215) * S
    bsd = lambda x, y: rr_sdf(x, y, S * 0.5, S * 0.5, bs, bs, br)

    def badge_col(x, y):
        t = clamp((y - bm) / bs)
        r = lerp(30.0, 15.0, t)
        g = lerp(41.0, 20.0, t)
        b = lerp(57.0, 29.0, t)
        return (r, g, b, 1.0)

    fill(bsd, badge_col)

    # soft top sheen
    def sheen_col(x, y):
        t = clamp((y - bm) / (bs * 0.55))
        return (255.0, 255.0, 255.0, 0.10 * (1.0 - t))

    fill(bsd, sheen_col)

    # badge border (accent)
    bw = max(0.90 * ss, pk(0.040, 0.048, 0.030) * S)
    fill(lambda x, y: abs(bsd(x, y)) - bw * 0.5,
         lambda x, y: (0.0, 170.0, 255.0, 0.80))

    # --- back rect: the source display (dim outline, upper-left) ----------
    pw = pk(0.400, 0.440, 0.450)
    ph = pk(0.300, 0.325, 0.345)
    off = pk(0.113, 0.125, 0.115)
    bcx = (0.5 - off) * S
    bcy = (0.5 - off) * S
    frw = pw * S
    frh = ph * S
    brad = pk(0.040, 0.055, 0.048) * S
    bsd2 = lambda x, y: rr_sdf(x, y, bcx, bcy, frw, frh, brad)
    bw2 = max(1.00 * ss, pk(0.055, 0.068, 0.045) * S)

    fill(lambda x, y: abs(bsd2(x, y)) - bw2 * 0.5,
         lambda x, y: (128.0, 152.0, 182.0, 0.95))

    # faint content lines inside it (only readable at large sizes)
    def scan_col(x, y):
        f = (y - (bcy - frh * 0.5)) / frh
        n = f * 3.6 - 1.0
        d = abs(n - round(n))
        if d > 0.11:
            return (0.0, 0.0, 0.0, 0.0)
        return (165.0, 190.0, 220.0, 0.20)

    if detail:
        fill(bsd2, scan_col)

    # --- front rect: the mirrored overlay (accent, lower-right) -----------
    fcx = (0.5 + off) * S
    fcy = (0.5 + off) * S
    frad = pk(0.040, 0.055, 0.048) * S
    fsd = lambda x, y: rr_sdf(x, y, fcx, fcy, frw, frh, frad)

    # drop shadow so the overlay clearly floats above the source
    for k in range(1, 4):
        o = k * 0.0075 * S
        fill(lambda x, y, o=o: rr_sdf(x, y, fcx + o, fcy + o * 1.3, frw, frh, frad),
             lambda x, y: (0.0, 0.0, 0.0, 0.115), soft=5.0)

    def front_col(x, y):
        t = clamp((y - (fcy - frh * 0.5)) / frh)
        return (0.0, lerp(184.0, 104.0, t), lerp(252.0, 196.0, t), 1.0)

    fill(fsd, front_col)

    # the SAME content lines, now in bright blue -> "this is the mirror image"
    def front_lines(x, y):
        f = (y - (fcy - frh * 0.5)) / frh
        n = (f - 0.30) * 3.1
        d = abs(n - round(n))
        if f < 0.34 or d > 0.085:
            return (0.0, 0.0, 0.0, 0.0)
        return (232.0, 250.0, 255.0, 0.22)

    if detail:
        fill(fsd, front_lines)
        # accent title bar across the top of the mirrored panel
        fill(lambda x, y: max(fsd(x, y), y - (fcy - frh * 0.5 + 0.075 * S)),
             lambda x, y: (238.0, 252.0, 255.0, 0.38))

    # bright top edge + outline keeps it crisp on dark and light backgrounds
    fill(lambda x, y: max(fsd(x, y), y - (fcy - frh * 0.5 + 1.6 * ss)),
         lambda x, y: (235.0, 252.0, 255.0, 0.55))
    fw = max(1.2 * ss, 0.018 * S)
    fill(lambda x, y: abs(fsd(x, y)) - fw * 0.5,
         lambda x, y: (8.0, 26.0, 44.0, 0.32))

    # --- downsample -------------------------------------------------------
    out = bytearray(size * size * 4)
    inv = 1.0 / (ss * ss)
    for y in range(size):
        for x in range(size):
            r = g = b = a = 0.0
            for sy in range(ss):
                base = ((y * ss + sy) * N + x * ss) * 4
                for sx in range(ss):
                    i = base + sx * 4
                    r += buf[i]; g += buf[i + 1]; b += buf[i + 2]; a += buf[i + 3]
            o = (y * size + x) * 4
            out[o] = int(clamp(r * inv / 255.0) * 255.0 + 0.5)
            out[o + 1] = int(clamp(g * inv / 255.0) * 255.0 + 0.5)
            out[o + 2] = int(clamp(b * inv / 255.0) * 255.0 + 0.5)
            out[o + 3] = int(clamp(a * inv) * 255.0 + 0.5)
    return bytes(out)


# ---------------------------------------------------------------- PNG
def png_bytes(w, h, rgba):
    raw = bytearray()
    stride = w * 4
    for y in range(h):
        raw.append(0)
        raw += rgba[y * stride:(y + 1) * stride]

    def chunk(tag, data):
        return (struct.pack('>I', len(data)) + tag + data +
                struct.pack('>I', zlib.crc32(tag + data) & 0xffffffff))

    ihdr = struct.pack('>IIBBBBB', w, h, 8, 6, 0, 0, 0)
    return (b'\x89PNG\r\n\x1a\n' + chunk(b'IHDR', ihdr) +
            chunk(b'IDAT', zlib.compress(bytes(raw), 9)) + chunk(b'IEND', b''))


def bmp_entry(w, h, rgba):
    """32bpp bottom-up DIB + empty AND mask, as stored inside an .ico."""
    stride = w * 4
    body = bytearray()
    for y in range(h - 1, -1, -1):
        row = rgba[y * stride:(y + 1) * stride]
        for i in range(0, stride, 4):
            body += bytes((row[i + 2], row[i + 1], row[i], row[i + 3]))  # RGBA -> BGRA
    mask_row = ((w + 31) // 32) * 4
    body += bytes(mask_row * h)
    hdr = struct.pack('<IiiHHIIiiII', 40, w, h * 2, 1, 32, 0, len(body), 0, 0, 0, 0)
    return bytes(hdr) + bytes(body)


# ---------------------------------------------------------------- main
SIZES = [16, 20, 24, 32, 40, 48, 64, 96, 128, 256]
PNG_FROM = 128          # store the big ones PNG-compressed to keep the exe small

def main():
    images = []
    for s in SIZES:
        rgba = render(s)
        if s >= PNG_FROM:
            images.append((s, png_bytes(s, s, rgba), True))
        else:
            images.append((s, bmp_entry(s, s, rgba), False))
        print('  rendered %dx%d  %s  %d bytes' % (s, s, 'PNG' if s >= PNG_FROM else 'BMP', len(images[-1][1])))

    # 256 preview for docs
    with open(os.path.join(OUT_DIR, 'app_256.png'), 'wb') as f:
        f.write(images[-1][1])

    n = len(images)
    out = bytearray(struct.pack('<HHH', 0, 1, n))
    off = 6 + 16 * n
    for s, data, _ in images:
        out += struct.pack('<BBBBHHII',
                           s if s < 256 else 0, s if s < 256 else 0,
                           0, 0, 1, 32, len(data), off)
        off += len(data)
    for _, data, _ in images:
        out += data

    p = os.path.join(OUT_DIR, 'app.ico')
    with open(p, 'wb') as f:
        f.write(bytes(out))
    print('\nwrote %s  (%d bytes, %d images)' % (p, len(out), n))
    print('wrote app_256.png')


if __name__ == '__main__':
    main()
