# -*- coding: utf-8 -*-
"""ICM 2023 E 配图：24 张 SVG，复用 _svg 基元，输出到 papers/。
视角划分：Paper1 指标构建与风险刻画 / Paper2 四类地点应用与干预 / Paper3 稳健性·政策·传单。
"""
import os
import math
import xml.dom.minidom
import gen_icm2023e as G

HERE = os.path.dirname(os.path.abspath(__file__))
OUT = os.path.normpath(os.path.join(HERE, "..", "assets", "problems", "papers"))
os.makedirs(OUT, exist_ok=True)

from _svg import (_fig, _save, _bar, _grouped_bar, _line, _scatter, _pie,
                  _heatmap, _network, _flow, _txt, C_RED, C_GREEN, C_ACC, C_CYAN,
                  C_AMB, C_PUR, C_TEAL, C_PINK, C_MUT, C_TXT, PALETTE)

C_BLUE = C_ACC; C_AMBER = C_AMB; C_PURPLE = C_PUR; C_SLATE = "#475569"
TYPE_COLOR = {"protected": C_TEAL, "rural": C_CYAN, "suburban": C_AMBER, "urban": C_RED}
TYPE_CN = G.TYPE_CN
INTERV_COLOR = {"I1": C_TEAL, "I2": C_AMBER, "I3": C_PURPLE}
LEVEL_COLOR = {"Low": C_GREEN, "Moderate": C_AMBER, "High": C_RED}

D = G.gen_icm2023e()
base = D["base"]
by_type = D["by_type"]
corr = D["corr"]
sens = D["sens"]
TYPES = D["TYPES"]

# 地点短码
SHORT = {}
for t in TYPES:
    k = 0
    for loc in base:
        if loc["type"] == t:
            k += 1
            SHORT[loc["id"]] = t[0].upper() + str(k)
NAMES = {loc["id"]: loc["name"] for loc in base}
TYPES_ORDER = sorted(base, key=lambda r: r["rank"])  # 按风险降序


def _loc(i):
    return next(r for r in base if r["id"] == i)


def _corr(a, b):
    m = len(a)
    ma = sum(a)/m; mb = sum(b)/m
    cov = sum((a[i]-ma)*(b[i]-mb) for i in range(m))
    sa = math.sqrt(sum((x-ma)**2 for x in a))
    sb = math.sqrt(sum((x-mb)**2 for x in b))
    return cov/(sa*sb) if sa*sb > 0 else 0.0


def _save_check(name, svg):
    xml.dom.minidom.parseString(svg)
    _save(os.path.join(OUT, name), svg)
    return name


def _type_means(ind):
    return [sum(r[ind] for r in base if r["type"] == t)/by_type[t]["n"] for t in TYPES]


# ============ Paper 1：指标构建与风险刻画 ============
def f1_1():
    indicators = ["L", "G", "P", "B", "S", "R"]
    rows = ["L 亮度", "G 增长", "P 人口暴露", "B 生态敏感", "S 安全需求", "R 治理成熟"]
    M = []
    for ind in indicators:
        M.append([r[ind] for r in base])
    cols = [SHORT[r["id"]] for r in base]
    return _heatmap("图1 16 个代表地点的 6 项光污染指标面板（红=高）",
                    rows, cols, M, fmt="%.2f")


def f1_2():
    inds = ["L", "G", "P", "B", "S", "R"]
    groups = [TYPE_CN[t] for t in TYPES]
    cats = ["L亮度", "G增长", "P人口", "B生态", "S安全", "R治理"]
    vals = [_type_means(ind) for ind in inds]
    return _grouped_bar("图2 四类地点的指标均值对比（城市最亮、保护地最敏感）",
                        groups, cats, vals, fmt="%.2f")


def f1_3():
    pts = [(r["L"], r["lpri"], TYPE_COLOR[r["type"]]) for r in base]
    series = []
    for t in TYPES:
        series.append((TYPE_CN[t], TYPE_COLOR[t],
                       [(r["L"], r["lpri"]) for r in base if r["type"] == t]))
    return _scatter("图3 夜间亮度 L 与 LPRI 风险（相关性 0.99，最强驱动）",
                    series, xlabel="夜间平均辐亮度 L", ylabel="LPRI 风险指数")


def f1_4():
    series = []
    for t in TYPES:
        series.append((TYPE_CN[t], TYPE_COLOR[t],
                       [(r["B"], r["er"]) for r in base if r["type"] == t]))
    return _scatter("图4 生态敏感性 B 与生态风险（相关性 −0.91）",
                    series, xlabel="生态/保护敏感性 B", ylabel="生态风险 EcoRisk")


def f1_5():
    labels = [SHORT[r["id"]] for r in TYPES_ORDER]
    vals_h = [r["hr"] for r in TYPES_ORDER]
    vals_e = [r["er"] for r in TYPES_ORDER]
    body = ""
    n = len(labels)
    bw = (760 - 140) / (n * 2 + 1)
    for i in range(n):
        x1 = 60 + (i * 2 + 1) * bw
        x2 = 60 + (i * 2 + 2) * bw
        h1 = vals_h[i] * (440 - 175)
        h2 = vals_e[i] * (440 - 175)
        body += '<rect x="%g" y="%g" width="%g" height="%g" fill="%s" rx="2"/>\n' % (x1, 440 - 105 - h1, bw, h1, C_BLUE)
        body += '<rect x="%g" y="%g" width="%g" height="%g" fill="%s" rx="2"/>\n' % (x2, 440 - 105 - h2, bw, h2, C_PURPLE)
        body += _txt(x1 + bw/2, 440 - 90, labels[i], 8, C_MUT)
    body += _txt(120, 57, "■ 人类风险", 11, C_BLUE, "start")
    body += _txt(220, 57, "■ 生态风险", 11, C_PURPLE, "start")
    body += _txt(380, 425, "柱高=风险分量（蓝=人类 紫=生态），按 LPRI 降序", 10, C_MUT)
    return _fig("图5 各地点人类风险 vs 生态风险分量", body, 760, 440)


def f1_6():
    labels = ["L", "G", "P", "B", "S", "R"]
    vals = [corr[k] for k in labels]
    W, H = 760, 440
    y0 = H - 120
    scale = (H - 200)
    body = ""
    n = len(labels)
    bw = (W - 140) / (n + 1)
    for i, lb in enumerate(labels):
        x = 60 + (i + 1) * bw
        v = vals[i]
        h = abs(v) * scale
        if v >= 0:
            y = y0 - h
            c = C_RED
        else:
            y = y0
            c = C_GREEN
        body += '<rect x="%g" y="%g" width="%g" height="%g" fill="%s" rx="2"/>\n' % (x, y, bw * 0.82, h, c)
        body += _txt(x + bw * 0.41, y - 6 if v >= 0 else y + h + 12, "%.3f" % v, 10, C_TXT, "middle", "700")
        body += _txt(x + bw * 0.41, H - 90, lb, 11, C_MUT)
    body += '<line x1="40" y1="%g" x2="%d" y2="%g" stroke="#9ca3af" stroke-width="2"/>\n' % (y0, W - 40, y0)
    body += _txt(W / 2, H - 14, "柱高=相关系数（红正 绿负，0 线居中）", 11, C_MUT)
    return _fig("图6 六项指标与 LPRI 的 Pearson 相关性（红正 绿负）", body, W, H)


def f1_7():
    xs = [s["we"] for s in sens]
    ys = [s["flips"] for s in sens]
    pts = [(xs[i], ys[i]) for i in range(len(xs))]
    return _line("图7 权重敏感性：生态权重 w_e 变动下的分级翻转数",
                 [("翻转数", C_RED, pts)], xlabel="生态权重 w_e（人类权重=1−w_e）",
                 ylabel="分级翻转地点数", xmin=0.3, xmax=0.7, ymin=0,
                 ymax=max(ys) + 1)


def f1_8():
    steps = [("六指标刻画", "L/G/P/B/S/R"),
             ("归一化", "统一[0,1]"),
             ("两分量风险", "人类/生态"),
             ("LPRI 综合", "等权0.5/0.5"),
             ("风险分级", "低/中/高"),
             ("干预优化", "三类策略")]
    return _flow("图8 光污染风险指标体系方法论流程", steps, W=820, H=300)


# ============ Paper 2：四类地点应用与干预 ============
def f2_1():
    labels = [SHORT[r["id"]] for r in TYPES_ORDER]
    vals = [r["lpri"] for r in TYPES_ORDER]
    colors = [TYPE_COLOR[r["type"]] for r in TYPES_ORDER]
    return _bar("图1 16 个地点 LPRI 风险排名（颜色=地点类型，红带=高风险）",
                labels, vals, colors=colors, ylabel="LPRI", fmt="%.3f",
                ymax=0.7)


def f2_2():
    groups = [TYPE_CN[t] for t in TYPES]
    cats = ["低 Low", "中 Moderate", "高 High"]
    vals = [[by_type[t]["counts"]["Low"], by_type[t]["counts"]["Moderate"],
             by_type[t]["counts"]["High"]] for t in TYPES]
    return _grouped_bar("图2 四类地点的风险分级计数",
                        groups, cats, vals, colors=[C_GREEN, C_AMBER, C_RED], fmt="%d")


def f2_3():
    picks = [3, 13]
    groups = [NAMES[i] for i in picks]
    cats = ["现状", "I1遮光", "I2调光", "I3分区"]
    vals = []
    for i in picks:
        loc = _loc(i)
        row = [loc["lpri"]]
        for key in ["I1", "I2", "I3"]:
            row.append(loc["interv"][key]["lpri2"])
        vals.append(row)
    return _grouped_bar("图3 选点干预前后 LPRI（reserve C 选 I3，城市核心 M 选 I1）",
                        groups, cats, vals,
                        colors=[C_SLATE, C_TEAL, C_AMBER, C_PURPLE], fmt="%.3f")


def f2_4():
    keys = ["I1", "I2", "I3"]
    mean_d = []
    for key in keys:
        mean_d.append(sum(loc["interv"][key]["delta"] for loc in base)/len(base))
    colors = [INTERV_COLOR[k] for k in keys]
    return _bar("图4 三类干预在全样本的平均 LPRI 削减量",
                [G.INTERVENTIONS[k]["name"] for k in keys], mean_d,
                colors=colors, ylabel="平均 ΔLPRI", fmt="%.4f")


def f2_5():
    keys = ["I1", "I2", "I3"]
    return _scatter("图5 干预成本 vs 平均风险削减（点色=策略）",
                    [(G.INTERVENTIONS[k]["name"], INTERV_COLOR[k],
                      [(G.INTERVENTIONS[k]["cost"],
                        sum(loc["interv"][k]["delta"] for loc in base)/len(base))])
                     for k in keys],
                    xlabel="相对成本", ylabel="平均 ΔLPRI")


def f2_6():
    rows = TYPES_ORDER
    labels = [SHORT[r["id"]] for r in rows]
    colors = [INTERV_COLOR[r["best"]] for r in rows]
    body = ""
    n = len(rows)
    bw = (760 - 140) / (n + 1)
    for i, r in enumerate(rows):
        x = 60 + (i + 1) * bw
        h = (1.0 / 3.0) * (440 - 175)
        body += '<rect x="%g" y="%g" width="%g" height="%g" fill="%s" rx="2"/>\n' % (
            x, 440 - 105 - h, bw * 0.82, h, colors[i])
        body += _txt(x + bw * 0.41, 440 - 111 - h, r["best"], 9, C_TXT, "middle", "700")
        body += _txt(x + bw * 0.41, 440 - 86, labels[i], 8, C_MUT)
    body += _txt(120, 57, "■ I1遮光+LED", 11, C_TEAL, "start")
    body += _txt(240, 57, "■ I2调光+宵禁", 11, C_AMBER, "start")
    body += _txt(360, 57, "■ I3分区+廊道", 11, C_PURPLE, "start")
    body += _txt(380, 425, "柱顶标注各地点最优干预策略", 10, C_MUT)
    return _fig("图6 各地点最优干预策略映射（颜色=胜出策略）", body, 760, 440)


def f2_7():
    keys = ["I1", "I2", "I3"]
    rows = [SHORT[r["id"]] for r in TYPES_ORDER]
    M = [[r["interv"][k]["delta"] for k in keys] for r in TYPES_ORDER]
    return _heatmap("图7 各地点 × 三干预的 LPRI 削减量（红=削减越大）",
                    rows, keys, M, fmt="%.3f")


def f2_8():
    steps = [("现状评估", "LPRI分级"),
             ("列干预", "I1/I2/I3"),
             ("算削减", "ΔLPRI"),
             ("选最优", "max Δ"),
             ("评成本", "Δ/成本"),
             ("落地", "政策建议")]
    return _flow("图8 干预策略优选流程", steps, W=820, H=300)


# ============ Paper 3：稳健性·政策·传单 ============
def f3_1():
    xs, ys = [], []
    for we in [0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70]:
        wh = 1 - we
        nh = sum(1 for r in base if G.level(G.lpri(r["L"], r["G"], r["P"], r["B"], r["S"], r["R"], wh, we)) == "High")
        xs.append(we); ys.append(nh)
    pts = [(xs[i], ys[i]) for i in range(len(xs))]
    return _line("图1 生态权重 w_e 变动下的高风险地点数（0.4–0.6 稳定）",
                 [("高风险数", C_RED, pts)], xlabel="生态权重 w_e",
                 ylabel="High 类地点数", xmin=0.3, xmax=0.7, ymin=0, ymax=max(ys) + 1)


def f3_2():
    inds = ["L", "G", "P", "B", "S", "R"]
    rows = ["L", "G", "P", "B", "S", "R"]
    hr = [r["hr"] for r in base]; er = [r["er"] for r in base]; v = [r["lpri"] for r in base]
    M = [[_corr([r[ind] for r in base], tgt) for tgt in (v, hr, er)] for ind in inds]
    return _heatmap("图2 指标与 LPRI/人类风险/生态风险的相关系数矩阵",
                    rows, ["LPRI", "人类风险", "生态风险"], M, fmt="%.2f")


def f3_3():
    loc = _loc(3)
    ind = ["L", "G", "P", "B", "S", "R"]
    pre = [loc[i] for i in ind]
    L, Gg, P, B, S, R = loc["L"], loc["G"], loc["P"], loc["B"], loc["S"], loc["R"]
    L2, G2, P2, B2, S2, R2 = G.INTERVENTIONS["I3"]["f"](L, Gg, P, B, S, R)
    post = [L2, G2, P2, B2, S2, R2]
    return _grouped_bar("图3 城郊 reserve C（最优 I3）干预前后指标变化",
                        ind, ["干预前", "干预后"],
                        [[pre[i], post[i]] for i in range(6)],
                        colors=[C_SLATE, C_PURPLE], fmt="%.2f")


def f3_4():
    loc = _loc(13)
    ind = ["L", "G", "P", "B", "S", "R"]
    pre = [loc[i] for i in ind]
    L, Gg, P, B, S, R = loc["L"], loc["G"], loc["P"], loc["B"], loc["S"], loc["R"]
    L2, G2, P2, B2, S2, R2 = G.INTERVENTIONS["I1"]["f"](L, Gg, P, B, S, R)
    post = [L2, G2, P2, B2, S2, R2]
    return _grouped_bar("图4 城市核心 M（最优 I1）干预前后指标变化",
                        ind, ["干预前", "干预后"],
                        [[pre[i], post[i]] for i in range(6)],
                        colors=[C_SLATE, C_TEAL], fmt="%.2f")


def f3_5():
    # 一页宣传传单（城市核心 M + I1）
    W, H = 560, 760
    body = ""
    body += '<rect x="20" y="20" width="%d" height="%d" fill="#fffbeb" stroke="%s" stroke-width="3"/>\n' % (W - 40, H - 40, C_AMBER)
    body += _txt(W/2, 60, "守护城市的夜空", 26, C_RED, "middle", "800")
    body += _txt(W/2, 86, "城市核心区光污染干预行动倡议", 14, C_MUT, "middle", "700")
    body += _txt(40, 120, "问题", 16, C_RED, "start", "800")
    body += _txt(40, 150, "本地 LPRI 风险高达 0.603（高危险级），", 13, C_TXT, "start")
    body += _txt(40, 172, "过亮照明扰民睡眠、浪费能源、影响生态。", 13, C_TXT, "start")
    body += _txt(40, 210, "行动：I1 定向遮光 + 暖色 LED 替换", 16, C_TEAL, "start", "800")
    body += _txt(40, 240, "· 为灯具加装遮光罩，光只照地面不射天", 13, C_TXT, "start")
    body += _txt(40, 262, "· 用 3000K 暖色 LED 替代入射蓝紫光", 13, C_TXT, "start")
    body += _txt(40, 284, "· 重点改造主干道与楼顶广告照明", 13, C_TXT, "start")
    body += _txt(40, 322, "预期收益", 16, C_GREEN, "start", "800")
    body += _txt(40, 352, "· 风险指数下降 0.132（−22%）", 13, C_TXT, "start")
    body += _txt(40, 374, "· 年节电约 15%，减少眩光事故", 13, C_TXT, "start")
    body += _txt(40, 396, "· 暗夜回归，居民睡眠与生态双受益", 13, C_TXT, "start")
    body += _txt(40, 440, "加入我们：从一盏灯开始，把夜还给孩子与星空。", 13, C_PURPLE, "start", "700")
    body += _txt(W - 40, H - 60, "COMAP 照明控制任务 · 本地示范", 11, C_MUT, "end")
    return _fig("图5 一页宣传传单（城市核心 M × 干预 I1）", body, W, H)


def f3_6():
    means = []
    for t in TYPES:
        rows = [r for r in base if r["type"] == t]
        means.append(sum(r["best_delta"] for r in rows)/len(rows))
    colors = [TYPE_COLOR[t] for t in TYPES]
    return _bar("图6 四类地点的平均最优风险削减量（提示政策优先度）",
                [TYPE_CN[t] for t in TYPES], means, colors=colors,
                ylabel="平均 ΔLPRI", fmt="%.4f")


def f3_7():
    groups = [TYPE_CN[t] for t in TYPES]
    pre = [by_type[t]["mean_lpri"] for t in TYPES]
    post = []
    for t in TYPES:
        rows = [r for r in base if r["type"] == t]
        post.append(sum(r["lpri"] - r["best_delta"] for r in rows)/len(rows))
    return _grouped_bar("图7 四类地点干预前 vs 最优干预后平均 LPRI",
                        groups, ["干预前", "最优干预后"],
                        [[pre[i], post[i]] for i in range(4)],
                        colors=[C_SLATE, C_TEAL], fmt="%.3f")


def f3_8():
    steps = [("指标构建", "LPRI"),
             ("分类定位", "四类地点"),
             ("干预优选", "I1/I2/I3"),
             ("成本效益", "Δ/成本"),
             ("稳健验证", "权重敏感"),
             ("传单倡导", "落地传播")]
    return _flow("图8 光污染治理决策闭环", steps, W=820, H=300)


JOBS = [f1_1, f1_2, f1_3, f1_4, f1_5, f1_6, f1_7, f1_8,
        f2_1, f2_2, f2_3, f2_4, f2_5, f2_6, f2_7, f2_8,
        f3_1, f3_2, f3_3, f3_4, f3_5, f3_6, f3_7, f3_8]


def main():
    bad = 0
    for i, job in enumerate(JOBS, 1):
        try:
            svg = job()
            name = "icm2023e-%d-fig%d.svg" % ((i - 1) // 8 + 1, (i - 1) % 8 + 1)
            _save_check(name, svg)
        except Exception as e:
            bad += 1
            print("FAIL", i, job.__name__, e)
    print("icm2023e figures: %d generated, %d bad" % (len(JOBS), bad))


if __name__ == "__main__":
    main()
