# -*- coding: utf-8 -*-
"""在粉笔黑板图上叠中文标题/栏目名，模拟彩色粉笔字（逐字换色 + 白色粉笔描边 + 颗粒挖孔）"""
import os, numpy as np
from PIL import Image, ImageDraw, ImageFont, ImageFilter

BASE = '/data/www/files/heibanbaoChalk'
RAW, OUT = f'{BASE}/raw', f'{BASE}/final'
os.makedirs(OUT, exist_ok=True)
FONT = '/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc'
IDX = 2

RED, BLUE, PURPLE, ORANGE, GREEN, PINK, YELLOW = (
    (226, 64, 58), (92, 170, 226), (168, 126, 212), (245, 150, 60),
    (122, 196, 112), (240, 140, 182), (248, 214, 96))
CYCLE = [RED, BLUE, PURPLE, ORANGE, GREEN, PINK, YELLOW, BLUE]
WHITE = (250, 250, 246)

JOBS = {
 'A_综合版_安全反诈禁毒': ('安全小卫士', '安全教育 · 防范诈骗 · 禁毒教育',
                          ['安全教育', '防诈防骗', '远离毒品']),
 'B_校园场景版_安全反诈禁毒': ('校园平安行', '安全出行 · 反诈防骗 · 远离毒品',
                              ['安全出行', '反诈防骗', '远离毒品']),
 'C_三分区留白版_安全反诈禁毒': ('三防小课堂', '安全教育 · 防范诈骗 · 远离毒品',
                                ['安全教育', '反诈防骗', '远离毒品']),
}


def chalk_layer(text, fs, stroke_w, colors, vertical=False):
    """返回 RGBA 图层：逐字换色 + 白描边 + 粉笔颗粒"""
    f = ImageFont.truetype(FONT, fs, index=IDX)
    char_w = [f.getbbox(c)[2] for c in text] if not vertical else [fs] * len(text)
    total = sum(char_w) + (len(text) - 1) * int(fs * 0.06)
    W, H = total + stroke_w * 4, (fs * 1.5 if not vertical else fs * len(text) * 1.25) + stroke_w * 4
    layer = Image.new('RGBA', (int(W), int(H)), (0, 0, 0, 0))
    d = ImageDraw.Draw(layer)
    x = stroke_w * 2
    for i, c in enumerate(text):
        col = colors[i % len(colors)]
        if vertical:
            d.text((W / 2, stroke_w * 2 + i * fs * 1.25), c, font=f, fill=col,
                   stroke_width=stroke_w, stroke_fill=WHITE, anchor='ma')
        else:
            d.text((x, stroke_w * 2), c, font=f, fill=col,
                   stroke_width=stroke_w, stroke_fill=WHITE, anchor='la')
            x += char_w[i] + int(fs * 0.06)
    # 粉笔颗粒：alpha 随机起伏 + 少量挖孔
    a = np.array(layer).astype(np.float32)
    alpha = a[..., 3]
    rng = np.random.default_rng(7)
    noise = rng.normal(1.02, 0.16, alpha.shape)
    alpha = np.clip(alpha * noise, 0, 255)
    holes = rng.random(alpha.shape) < 0.05
    alpha[holes & (alpha > 40)] *= 0.15
    a[..., 3] = alpha
    out = Image.fromarray(a.clip(0, 255).astype(np.uint8), 'RGBA').filter(ImageFilter.GaussianBlur(0.6))
    return out


def paste_center(img, layer, cx, cy):
    img.alpha_composite(layer, (int(cx - layer.width / 2), int(cy - layer.height / 2)))


def fit_fs(text, target=2450, cap=300):
    n = len(text.replace(' ', '')) + text.count(' ') * 0.55
    return int(min(cap, target / max(n, 1)))


def emptiest_band(lum, x0, x1, y_lo, y_hi, band_h=90):
    """在给定纵区里找最空（最暗）的一条横带，返回中心 y"""
    best, best_y = -1, int(y_lo)
    for y in range(int(y_lo), int(y_hi) - band_h, 12):
        seg = lum[y:y + band_h, x0:x1]
        score = (seg < 78).mean()
        if score > best:
            best, best_y = score, y + band_h // 2
    return best_y, best


def main():
    for name, (title, sub, secs) in JOBS.items():
        src = os.path.join(RAW, name + '.jpg')
        if not os.path.exists(src):
            print('MISS', src); continue
        img = Image.open(src).convert('RGBA')
        w, h = img.size
        lum = np.asarray(img.convert('L')).astype(np.int16)

        # 主标题
        fs = fit_fs(title)
        lay = chalk_layer(title, fs, max(12, fs // 10), CYCLE)
        paste_center(img, lay, w / 2, int(h * 0.150))

        # 三个栏目名（不再叠副标题，避免与栏目名重复/相压）
        SEC_COLORS = [BLUE, YELLOW, RED]
        if not name.startswith('C'):
            # 先在纵区里找一条「左中右三区同时最空」的横带，保证三个栏目名排成一条线
            band_h, best_y, best = 92, int(h * 0.30), -1
            for y in range(int(h * 0.30), int(h * 0.46), 10):
                seg = lum[y:y + band_h]
                score = min((seg[:, int(w * i / 3) + 40:int(w * (i + 1) / 3) - 40] < 78).mean()
                            for i in range(3))
                if score > best:
                    best, best_y = score, y + band_h // 2
            common_y = best_y
            print(f'  {name} 三区共同空带 y={common_y} (最空区占比 {best:.2f})')
        for i, s in enumerate(secs):
            if name.startswith('C'):
                cx = int(w * (i + 0.5) / 3)
                y = int(h * 0.371)
            else:
                cx = int(w * (i + 0.5) / 3)
                y = common_y
            lay3 = chalk_layer(s, 92, 8, [SEC_COLORS[i]])
            paste_center(img, lay3, cx, y)
            print(f'  {name} 栏目{i+1} 「{s}」 -> x={cx} y={y}')

        out = os.path.join(OUT, name + '.jpg')
        img.convert('RGB').save(out, quality=96, subsampling=0, dpi=(300, 300))
        print('OK', out, f'{os.path.getsize(out)/1048576:.2f}MB')


if __name__ == '__main__':
    main()
