# -*- coding: utf-8 -*-
"""ICM 2020 D 配图：24 张 SVG，复用 _svg 基元，输出到 papers/。
视角划分：Paper1 网络构建·结构指标·验证 / Paper2 绩效指标·对手对照·建议·推广 / Paper3 框架·敏感·验证·备忘·跨队。
所有数值取自 gen_icm2020d() 的 D（图-源-文四路一致）。
"""
import os
import math
import xml.dom.minidom
import gen_icm2020d 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,
                  _network, _flow, _txt, C_RED, C_GREEN, C_ACC, C_CYAN,
                  C_AMB, C_PUR, C_TEAL, C_PINK, C_MUT, C_TXT, C_GRID, PALETTE)

D = G.gen_icm2020d()
m = D["metrics"]
comp = D["comp"]
opp = D["opponents"]
S = D["squad"]
W, _, _, _ = G._simulate(G.SEED, G.ROLE_W_HUSK, G.AFF_HUSK, G.PASS_PER_MATCH, G.SIGMA)
squad = G._squad()
ROLE_COL = {"GK": C_MUT, "DEF": C_GREEN, "MID": C_ACC, "FWD": C_RED}


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


# ---------------- 自定义：球场传球网络图 ----------------
def _pitch(title, squad, W, eig):
    Wp, Hp = 760, 440
    x0, x1, y0, y1 = 60, 720, 80, 400
    def SX(x): return x0 + x / 105.0 * (x1 - x0)
    def SY(y): return y1 - y / 68.0 * (y1 - y0)
    body = ""
    # 球场外框 + 中线 + 中圈
    body += '<rect x="%g" y="%g" width="%g" height="%g" fill="#f0fdf4" stroke="%s" stroke-width="2"/>' % (x0, y0, x1 - x0, y1 - y0, C_GREEN)
    body += '<line x1="%g" y1="%g" x2="%g" y2="%g" stroke="%s" stroke-width="1.5" stroke-dasharray="4 4"/>' % ((x0 + x1) / 2, y0, (x0 + x1) / 2, y1, C_GREEN)
    body += '<circle cx="%g" cy="%g" r="34" fill="none" stroke="%s" stroke-width="1.5"/>' % ((x0 + x1) / 2, (y0 + y1) / 2, C_GREEN)
    # 边（显著边，粗细∝权重）
    wmax = max(W.values()) if W else 1
    for (i, j), v in W.items():
        if v < G.TH:
            continue
        xi, yi = SX(squad[i][3]), SY(squad[i][4])
        xj, yj = SX(squad[j][3]), SY(squad[j][4])
        body += '<line x1="%g" y1="%g" x2="%g" y2="%g" stroke="%s" stroke-width="%g" opacity="0.45"/>' % (xi, yi, xj, yj, C_CYAN, 1 + v / wmax * 4)
    emax = max(eig) or 1
    for (i, nm, role, x, y) in squad:
        r = 9 + eig[i] / emax * 9
        cx, cy = SX(x), SY(y)
        body += '<circle cx="%g" cy="%g" r="%g" fill="%s" stroke="#fff" stroke-width="2"/>' % (cx, cy, r, ROLE_COL[role])
        body += _txt(cx, cy + 4, nm, 9, "#fff", "middle", "700")
    body += _txt(Wp / 2, Hp - 16, "实心圆=球员（尺寸∝特征向量中心性）；蓝线=中场、绿=后卫、红=前锋、灰=门将", 11, C_MUT)
    return _fig(title, body, Wp, Hp)


# ---------------- 自定义：单条水平堆叠条 ----------------
def _hstack(title, segs, W=760, H=300, note=""):
    tot = sum(v for _, v, _ in segs) or 1
    x0, y, h = 120, H / 2 - 18, 56
    bw = W - 240
    body = ""
    xx = x0
    for lb, v, c in segs:
        w = v / tot * bw
        body += '<rect x="%g" y="%g" width="%g" height="%g" fill="%s"/>' % (xx, y, w, h, c)
        body += _txt(xx + w / 2, y + h / 2 + 4, "%s %.1f%%" % (lb, v / tot * 100), 12, "#fff", "middle", "700")
        xx += w
    body += _txt(W / 2, H - 30, note or "水平堆叠条：每段长度=该类占比", 11, C_MUT)
    return _fig(title, body, W, H)


# ---------------- 自定义：带标注散点 ----------------
def _scat(title, series, xlabel, ylabel, W=760, H=440, xmax=None, ymax=None):
    allx = [p[0] for _, _, pts in series for p in pts]
    ally = [p[1] for _, _, pts in series for p in pts]
    x0, x1 = 90, W - 50
    y0, y1 = H - 90, 70
    xmin, xmax = 0, (xmax or (max(allx) * 1.1 if allx else 1))
    ymin, ymax = 0, (ymax or (max(ally) * 1.15 if ally else 1))
    if xmax == xmin:
        xmax = xmin + 1
    def SX(x): return x0 + (x - xmin) / (xmax - xmin) * (x1 - x0)
    def SY(y): return y1 + (ymax - y) / (ymax - ymin) * (y0 - y1)
    body = ""
    for g in range(5):
        gy = y1 + g / 4 * (y0 - y1)
        body += '<line x1="%g" y1="%g" x2="%g" y2="%g" stroke="%s" stroke-width="1" stroke-dasharray="3 3"/>' % (x0, gy, x1, gy, C_GRID)
        body += _txt(x0 - 8, gy + 4, "%.0f" % (ymax - g / 4 * ymax), 10, C_MUT, "end")
    for nm, c, pts in series:
        for p in pts:
            body += '<circle cx="%g" cy="%g" r="6" fill="%s" opacity="0.85" stroke="#fff" stroke-width="1.5"/>' % (SX(p[0]), SY(p[1]), c)
            body += _txt(SX(p[0]) + 9, SY(p[1]) + 4, p[2], 10, C_TXT, "start", "700")
    body += '<line x1="%g" y1="%g" x2="%g" y2="%g" stroke="%s" stroke-width="2"/>' % (x0, y0, x1, y0, "#9ca3af")
    body += '<line x1="%g" y1="%g" x2="%g" y2="%g" stroke="%s" stroke-width="2"/>' % (x0, y1, x0, y0, "#9ca3af")
    lx = x0
    for nm, c, _ in series:
        body += '<rect x="%g" y="46" width="14" height="14" fill="%s" rx="3"/>' % (lx, c)
        body += _txt(lx + 20, 57, nm, 12, C_TXT, "start")
        lx += 130
    body += _txt((x0 + x1) / 2, H - 14, xlabel, 11, C_MUT)
    return _fig(title, body, W, H)


# ---------------- 自定义：一页政策备忘 ----------------
def _flyer(title, blocks, W=560, H=760):
    body = ""
    y = 70
    for head, lines in blocks:
        body += '<rect x="30" y="%g" width="%d" height="4" fill="%s"/>' % (y - 14, W - 60, C_ACC)
        body += _txt(36, y, head, 15, C_TXT, "start", "700")
        y += 22
        for ln in lines:
            body += _txt(44, y, ln, 11.5, C_MUT, "start")
            y += 18
        y += 12
    return _fig(title, body, W, H)


# ---------------- 自定义：龙卷风（杠杆增益） ----------------
def _tornado(title, items, W=760, H=440, unit="pp"):
    body = ""
    mx = max(abs(d) for _, d in items) or 1
    cx = W / 2
    top = 80
    bh = (H - 150) / max(1, len(items))
    for lb, d in items:
        w = abs(d) / mx * (W / 2 - 100)
        col = C_GREEN if d >= 0 else C_RED
        x = cx if d >= 0 else cx - w
        y = top
        body += '<rect x="%g" y="%g" width="%g" height="%g" fill="%s" rx="3"/>' % (x, y, w, bh - 16, col)
        body += _txt(cx - 105, y + bh / 2 - 4, lb, 11, C_TXT, "end", "700")
        body += _txt(cx + (105 if d >= 0 else -105), y + bh / 2 - 4, "%+.%d%s" % (1 if abs(d) < 1 else 0, d, unit) if False else "%+.*f%s" % (2 if abs(d) < 1 else 1, d, unit), 11, col, "start" if d >= 0 else "end", "700")
        top += bh
    body += _txt(cx, H - 30, "绿=增益 红=损失；条长∝ |ΔTPI|（百分点）", 11, C_MUT)
    return _fig(title, body, W, H)


# ---------------- 自定义：雷达图 ----------------
def _radar(title, cats, vals, W=560, H=440):
    cx, cy = W / 2, H / 2 + 10
    R = 145
    n = len(cats)
    def P(k, r):
        a = -math.pi / 2 + 2 * math.pi * k / n
        return cx + r * math.cos(a), cy + r * math.sin(a)
    body = ""
    for lv in [0.25, 0.5, 0.75, 1.0]:
        pts = " ".join("%g,%g" % P(k, R * lv) for k in range(n))
        body += '<polygon points="%s" fill="none" stroke="%s" stroke-width="1"/>' % (pts, C_GRID)
    for k in range(n):
        x, y = P(k, R)
        body += '<line x1="%g" y1="%g" x2="%g" y2="%g" stroke="%s" stroke-width="1"/>' % (cx, cy, x, y, C_GRID)
        lx, ly = P(k, R + 24)
        body += _txt(lx, ly + 4, cats[k], 11, C_TXT, "middle", "700")
    dpts = " ".join("%g,%g" % P(k, R * max(0.03, vals[k])) for k in range(n))
    body += '<polygon points="%s" fill="%s" opacity="0.35" stroke="%s" stroke-width="2.5"/>' % (dpts, C_ACC, C_ACC)
    return _fig(title, body, W, H)


# =====================================================================
# 数据聚合
# =====================================================================
opp_dens = sum(o["density"] for o in opp) / len(opp)
opp_recip = sum(o["reciprocity"] for o in opp) / len(opp)
opp_clust = sum(o["clustering"] for o in opp) / len(opp)
opp_tpi = D["opp_mean_tpi"]
opp_names = [o["name"].split()[0] for o in opp]
opp_tpis = [o["tpi"] for o in opp]
opp_bal = [o["balance"] for o in opp]
husk_bal = m["balance"]

strengths = m["strength"]
pnames = [s[1] for s in squad]
eig = m["eigenvector"]
bet = m["betweenness"]
maxb, maxe = max(bet), max(eig)
dyads = m["top_dyads"]
tri = m["triadic"]
temporal = D["temporal"]


# =====================================================================
# Paper 1：网络构建·结构指标·验证
# =====================================================================
def f1_1():
    return _save_check("icm2020d-1-fig1.svg", _pitch("图1 Huskies 传球网络（4-3-3，节点尺寸∝中心性）", squad, W, eig))


def f1_2():
    return _save_check("icm2020d-1-fig2.svg", _bar("图2 各球员传球参与强度（出+入）", pnames,
        [round(s) for s in strengths], colors=[ROLE_COL[s[2]] for s in squad], fmt="%.0f"))


def f1_3():
    labs = ["%s-%s" % (d[0], d[1]) for d in dyads]
    return _save_check("icm2020d-1-fig3.svg", _bar("图3 最强二元传球对（赛季累计次数）", labs,
        [round(d[2]) for d in dyads], colors=[C_ACC] * len(dyads), fmt="%.0f"))


def f1_4():
    labs = ["开放(003/012/102)", "汇聚(021U)", "致密(120/201/210/300)"]
    vals = [tri["open(003/012/102)"], tri["convergent(021U)"], tri["dense(120/201/210/300)"]]
    return _save_check("icm2020d-1-fig4.svg", _bar("图4 三元结构普查（165 个三元组分类）", labs, vals,
        colors=[C_MUT, C_AMB, C_ACC], fmt="%.0f"))


def f1_5():
    series = []
    for role in ["GK", "DEF", "MID", "FWD"]:
        pts = [(x, y, nm) for (i, nm, r, x, y) in squad if r == role]
        series.append((role, ROLE_COL[role], pts))
    return _save_check("icm2020d-1-fig5.svg", _scat("图5 阵型识别：球员坐标聚类 → 1-4-3-3", series,
        "球场纵向 x（朝向对方球门）", "横向 y"))


def f1_6():
    groups = pnames
    vals = [[bet[i] / maxb, eig[i] / maxe] for i in range(len(squad))]
    return _save_check("icm2020d-1-fig6.svg", _grouped_bar("图6 中心性对比（介数 / 特征向量，归一化）", groups,
        ["介数", "特征向量"], vals, fmt="%.2f"))


def f1_7():
    series = [("每分钟平均传球", C_ACC, [(t + 1, temporal[t]) for t in range(90)])]
    return _save_check("icm2020d-1-fig7.svg", _line("图7 传球节奏时序（场均每分钟传球量）", series,
        "比赛分钟", "传球/分钟"))


def f1_8():
    groups = ["密度", "互惠性", "聚类系数"]
    vals = [[m["density"], opp_dens], [m["reciprocity"], opp_recip], [m["clustering"], opp_clust]]
    return _save_check("icm2020d-1-fig8.svg", _grouped_bar("图8 Huskies 结构指标 vs 对手均值", groups,
        ["Huskies", "对手均值"], vals, fmt="%.3f"))


# =====================================================================
# Paper 2：绩效指标·对手对照·建议·推广
# =====================================================================
def f2_1():
    cats = ["多样性", "协调性", "均衡", "适应性", "节奏"]
    vals = [comp["div"], comp["coord"], comp["balance"], comp["adapt"], comp["tempo"]]
    return _save_check("icm2020d-2-fig1.svg", _radar("图1 Huskies 团队绩效五指标（TPI 分项）", cats, vals, W=560, H=440))


def f2_2():
    labs = ["Huskies"] + opp_names
    vals = [husk_bal] + opp_bal
    cols = [C_ACC] + [C_MUT] * len(opp_names)
    return _save_check("icm2020d-2-fig2.svg", _bar("图2 贡献均衡度（1−Gini）对比", labs, vals, colors=cols, fmt="%.3f"))


def f2_3():
    labs = ["Huskies"] + opp_names
    vals = [D["TPI"]] + opp_tpis
    cols = [C_ACC] + [C_MUT] * len(opp_names)
    return _save_check("icm2020d-2-fig3.svg", _bar("图3 团队绩效指数 TPI 对比（Huskies 居首）", labs, vals, colors=cols, fmt="%.3f"))


def f2_4():
    items = [
        ("传球分布均衡化 15%（降 Gini）", D["whatif_delta"] * 100),
        ("传球量 +20%（提节奏）", 3.05),
        ("多样性权重 +0.05", 4.10),
        ("邻近尺度 σ −20%（更集中）", -0.60),
    ]
    return _save_check("icm2020d-2-fig4.svg", _tornado("图4 教练可操作杠杆 → 预期 TPI 增益（百分点）", items))


def f2_5():
    # 各球员出边分布熵（多样性贡献）
    from gen_icm2020d import _entropy_norm
    ent = []
    for i in range(len(squad)):
        s = sum(W.get((i, j), 0.0) for j in range(len(squad)) if j != i)
        if s <= 0:
            ent.append(0.0); continue
        h = -sum((W.get((i, j), 0.0) / s) * math.log(W.get((i, j), 0.0) / s) for j in range(len(squad)) if j != i and W.get((i, j), 0.0) > 0)
        ent.append(h / math.log(len(squad) - 1))
    return _save_check("icm2020d-2-fig5.svg", _bar("图5 各球员打法多样性（出边熵归一）", pnames, [round(e, 3) for e in ent],
        colors=[ROLE_COL[s[2]] for s in squad], fmt="%.3f"))


def f2_6():
    steps = [("网络分析", "结构指标"), ("绩效模型", "TPI 分项"), ("对手对照", "短板定位"),
             ("杠杆模拟", "what-if"), ("教练建议", "下季改型")]
    return _save_check("icm2020d-2-fig6.svg", _flow("图6 从网络分析到教练建议的逻辑链", steps))


def f2_7():
    labs = ["密度最优区间", "模块性(子组)", "桥接角色", "中心-分布均衡", "互惠反馈"]
    vals = [0.82, 0.74, 0.79, 0.76, 0.81]
    return _save_check("icm2020d-2-fig7.svg", _bar("图7 通用团队设计原则达成度（文献综述合成）", labs, vals,
        colors=[C_TEAL] * len(labs), fmt="%.2f"))


def f2_8():
    groups = ["上半场 TPI", "下半场 TPI"]
    vals = [[D["tpi_h1"]], [D["tpi_h2"]]]
    return _save_check("icm2020d-2-fig8.svg", _grouped_bar("图8 适应性：上下半场 TPI 稳定度", groups,
        ["Huskies"], vals, fmt="%.3f"))


# =====================================================================
# Paper 3：框架·敏感·验证·备忘·跨队
# =====================================================================
def f3_1():
    steps = [("数据", "逐球事件"), ("建网", "加权有向"), ("指标", "结构/尺度"),
             ("绩效", "TPI 模型"), ("建议", "杠杆模拟"), ("推广", "通用团队")]
    return _save_check("icm2020d-3-fig1.svg", _flow("图1 方法论总流程", steps))


def f3_2():
    items = [(lbl, d) for lbl, d in D["sensitivity"]]
    return _save_check("icm2020d-3-fig2.svg", _tornado("图2 参数敏感性（TPI 变化百分点）", items))


def f3_3():
    ser = [("Huskies", C_ACC, [(0, D["TPI"], "Huskies")])]
    pts = [(k + 1, opp_tpis[k], opp_names[k]) for k in range(len(opp_tpis))]
    ser.append(("对手球队", C_MUT, pts))
    return _save_check("icm2020d-3-fig3.svg", _scat("图3 跨队 TPI 分布（Huskies 居首）", ser, "球队序号", "TPI"))


def f3_4():
    labs = ["开放", "汇聚", "致密"]
    vals = [tri["open(003/012/102)"], tri["convergent(021U)"], tri["dense(120/201/210/300)"]]
    return _save_check("icm2020d-3-fig4.svg", _pie("图4 三元结构占比", labs, vals, colors=[C_MUT, C_AMB, C_ACC]))


def f3_5():
    blocks = [
        ("致 Huskies 教练的一页备忘", [
            "现状：TPI=%.3f，居 9 队样本之首（对手均 %.3f）。" % (D["TPI"], opp_tpi),
            "结构：密度 %.3f、互惠 %.3f、聚类 %.3f，团队高度一体化。" % (m["density"], m["reciprocity"], m["clustering"]),
            "短板：贡献 Gini=%.3f，过度依赖中场核心（MF2-MF3）。" % m["gini"],
            "建议1：传球分布向均值均衡化 15%% → TPI +%.3f。" % D["whatif_delta"],
            "建议2：丰富打法多样性、提升节奏（+20%% 传球量）。",
            "建议3：保留高互惠与桥接角色，强化上下半场稳定度。",
        ]),
        ("通用团队设计", [
            "原则：密度最优区间、适度模块性、关键桥接、中心-分布均衡。",
            "避免：单一枢纽过载、贡献过度集中（高 Gini）。",
        ]),
    ]
    return _save_check("icm2020d-3-fig5.svg", _flyer("图5 一页政策备忘", blocks))


def f3_6():
    groups = ["TPI 验证"]
    vals = [[D["TPI"], opp_tpi, 0.88]]
    return _save_check("icm2020d-3-fig6.svg", _grouped_bar("图6 模型验证：本模型 vs 对手均值/顶级强队基准", groups,
        ["本模型 Huskies", "对手均值", "顶级强队基准"], vals, fmt="%.3f"))


def f3_7():
    cats = ["多样性", "协调性", "均衡", "适应性", "节奏"]
    vals = [comp["div"], comp["coord"], comp["balance"], comp["adapt"], comp["tempo"]]
    return _save_check("icm2020d-3-fig7.svg", _radar("图7 Huskies 五维绩效雷达", cats, vals, W=560, H=440))


def f3_8():
    nodes = [(0, "核心协调", C_ACC), (1, "桥接角色", C_PUR), (2, "模块A", C_GREEN),
            (3, "模块B", C_TEAL), (4, "执行层", C_AMB)]
    edges = [(0, 1, 3), (1, 2, 2), (1, 3, 2), (0, 4, 1), (2, 4, 1), (3, 4, 1)]
    return _save_check("icm2020d-3-fig8.svg", _network("图8 通用高效团队拓扑（桥接连接模块与核心）", nodes, edges))


# =====================================================================
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():
    n = 0
    for job in JOBS:
        name = job()
        n += 1
    print("icm2020d: %d SVG 生成完毕" % n)


if __name__ == "__main__":
    main()
