"""產生左側圖示列的圖示：從設計稿 sidebar-source*.jpg 裁出彩色圖示、去背，
下方加上小字標籤。

用法：python3 -I packaging/icon/make_sidebar_icons.py
輸出：resources/icons/sidebar-<docs|chat>-<light|dark>@<1|2>x.png，編譯時由
src/shared/sidebar.rs 用 include_bytes! 打包進執行檔。

設計稿是 JPG（沒有透明度），去背做法：從裁切框外圍往內 flood fill，跟
背景色夠接近、而且跟外圍連通的像素才算背景（圖示裡的白紙不會被挖掉）；
邊緣依跟背景的色差給半透明，並把背景色從邊緣像素裡扣掉，貼到任何底色上
都不會有一圈白邊 / 黑邊。

尺寸單位是 pt（1x = 1px、2x = 2px）。需要 Pillow 與 macOS 的 PingFang 字型。
"""

import glob
import os
from collections import deque

from PIL import Image, ImageDraw, ImageFont

HERE = os.path.dirname(__file__)
SOURCE = os.path.join(HERE, "sidebar-source.jpg")
SOURCE_MEDIA = os.path.join(HERE, "sidebar-source-media.jpg")
SOURCE_REMOTE = os.path.join(HERE, "sidebar-source-remote.jpg")
OUT = os.path.join(HERE, "..", "..", "resources", "icons")

W, H = 56, 52            # 整張圖（pt）
ICON = 32                # 圖示最長邊（pt）
ICON_TOP = 2
LABEL_TOP = 37
LABEL_SIZE = 10

# 設計稿上各圖示的（來源檔, 裁切框），裁切框是原圖 px、留一點邊
CROPS = {
    ("docs", "light"): (SOURCE, (165, 535, 610, 950)),
    ("chat", "light"): (SOURCE, (765, 535, 1210, 955)),
    ("docs", "dark"): (SOURCE, (1530, 545, 1990, 950)),
    ("chat", "dark"): (SOURCE, (2140, 535, 2590, 955)),
    ("media", "light"): (SOURCE_MEDIA, (760, 505, 1215, 960)),
    ("media", "dark"): (SOURCE_MEDIA, (2120, 490, 2610, 980)),
    ("remote", "light"): (SOURCE_REMOTE, (760, 505, 1215, 960)),
    ("remote", "dark"): (SOURCE_REMOTE, (2115, 490, 2620, 980)),
}
LABELS = {"docs": "辦公", "chat": "聊天", "media": "影片", "remote": "遠端"}
TEXT = {"light": (29, 29, 31), "dark": (245, 245, 247)}   # 系統主要文字色

BG_TOLERANCE = 18        # 色差（三通道絕對差總和）在這以內一定是背景
EDGE_SOFTNESS = 40       # 再往上這麼多色差之內是半透明的邊緣


def font(size_px):
    for path in glob.glob("/System/Library/AssetsV2/*/*/AssetData/PingFang.ttc"):
        for index in range(20):
            try:
                f = ImageFont.truetype(path, size_px, index=index)
            except OSError:
                break
            if f.getname() == ("PingFang TC", "Medium"):
                return f
    return ImageFont.truetype("/System/Library/Fonts/STHeiti Medium.ttc", size_px)


def cutout(img):
    """回傳去背後的 RGBA，並裁到圖示實際範圍。"""
    img = img.convert("RGB")
    w, h = img.size
    px = img.load()
    corners = [px[0, 0], px[w - 1, 0], px[0, h - 1], px[w - 1, h - 1]]
    bg = tuple(sum(c[i] for c in corners) // 4 for i in range(3))

    # 淺色背景上，比背景還亮的像素（資料夾裡的白紙）一定是圖示的一部分：
    # 白紙跟淺灰背景色差太小，只看色差會被當成背景挖掉
    light_bg = sum(bg) > 600

    def diff(p):
        if light_bg and all(p[i] - bg[i] > 4 for i in range(3)):
            return 999
        return sum(abs(a - b) for a, b in zip(p, bg))

    # 跟外圍連通、色差在 BG_TOLERANCE + EDGE_SOFTNESS 內的都是背景或邊緣
    limit = BG_TOLERANCE + EDGE_SOFTNESS
    outside = [[False] * h for _ in range(w)]
    queue = deque((x, y) for x in range(w) for y in (0, h - 1))
    queue.extend((x, y) for y in range(h) for x in (0, w - 1))
    while queue:
        x, y = queue.popleft()
        if outside[x][y] or diff(px[x, y]) > limit:
            continue
        outside[x][y] = True
        for nx, ny in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
            if 0 <= nx < w and 0 <= ny < h and not outside[nx][ny]:
                queue.append((nx, ny))

    out = Image.new("RGBA", (w, h))
    opx = out.load()
    for x in range(w):
        for y in range(h):
            p = px[x, y]
            if not outside[x][y]:
                opx[x, y] = p + (255,)
                continue
            a = max(0.0, min(1.0, (diff(p) - BG_TOLERANCE) / EDGE_SOFTNESS))
            if a <= 0:
                opx[x, y] = bg + (0,)
            else:
                # 把背景色從半透明邊緣扣掉：p = a*c + (1-a)*bg
                c = tuple(max(0, min(255, round((p[i] - (1 - a) * bg[i]) / a))) for i in range(3))
                opx[x, y] = c + (round(a * 255),)
    return out.crop(out.getbbox())


def compose(icon, label, text_rgb, scale):
    """把圖示縮到 ICON pt、置中放上面，標籤置中放下面。scale = 1 或 2。"""
    canvas = Image.new("RGBA", (W * scale, H * scale), (0, 0, 0, 0))
    iw, ih = icon.size
    k = ICON * scale / max(iw, ih)
    small = icon.resize((round(iw * k), round(ih * k)), Image.LANCZOS)
    x = (canvas.width - small.width) // 2
    y = ICON_TOP * scale + (ICON * scale - small.height) // 2
    canvas.alpha_composite(small, (x, y))
    d = ImageDraw.Draw(canvas)
    f = font(LABEL_SIZE * scale)
    bbox = d.textbbox((0, 0), label, font=f)
    tx = (canvas.width - (bbox[2] - bbox[0])) / 2 - bbox[0]
    d.text((tx, LABEL_TOP * scale - bbox[1]), label, font=f, fill=text_rgb + (255,))
    return canvas


def main():
    os.makedirs(OUT, exist_ok=True)
    for (name, theme), (source, box) in CROPS.items():
        icon = cutout(Image.open(source).crop(box))
        # 只有圖示、不含標籤的版本（外框網頁 sidebar.html 用，標籤是 HTML 文字）
        k = 64 / max(icon.size)
        only = icon.resize((round(icon.width * k), round(icon.height * k)), Image.LANCZOS)
        canvas = Image.new("RGBA", (64, 64), (0, 0, 0, 0))
        canvas.alpha_composite(only, ((64 - only.width) // 2, (64 - only.height) // 2))
        path = os.path.join(OUT, f"side-{name}-{theme}.png")
        canvas.save(path)
        print(path)
        for scale in (1, 2):
            path = os.path.join(OUT, f"sidebar-{name}-{theme}@{scale}x.png")
            compose(icon, LABELS[name], TEXT[theme], scale).save(path)
            print(path)


if __name__ == "__main__":
    main()
