# -*- coding: utf-8 -*-
"""
Generate work-summary PPT styled like 组会/yh -散射边界条件.pptx
Run: python generate_summary_ppt.py
"""
from pathlib import Path
import subprocess
import sys

from pptx import Presentation
from pptx.util import Pt
from pptx.enum.text import PP_ALIGN

ROOT = Path(__file__).resolve().parent
ASSETS = ROOT / "ppt_assets"
REF_PPT = Path(r"e:\组会\yh -散射边界条件.pptx")
REF_IMGS = Path(r"c:\Users\Administrator\Desktop\ppt_assets_ref")
OUT_PATH = ROOT / "OpticsFEM_2D_to_3D_Summary.pptx"
LAYOUT_TITLE_CONTENT = 11  # 5_标题和内容 — same as reference deck


def delete_all_slides(prs):
    while len(prs.slides) > 0:
        sld_id = prs.slides._sldIdLst[0]
        r_id = sld_id.rId
        prs.part.drop_rel(r_id)
        del prs.slides._sldIdLst[0]


def set_title(slide, text):
    if slide.shapes.title:
        slide.shapes.title.text = text


def add_textbox(slide, left, top, width, height, text, size=14, bold=False):
    box = slide.shapes.add_textbox(left, top, width, height)
    tf = box.text_frame
    tf.word_wrap = True
    p = tf.paragraphs[0]
    p.text = text
    p.font.size = Pt(size)
    p.font.bold = bold
    return box


def add_bullets(slide, left, top, width, height, items, size=13):
    box = slide.shapes.add_textbox(left, top, width, height)
    tf = box.text_frame
    tf.word_wrap = True
    for i, item in enumerate(items):
        p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
        p.text = item
        p.font.size = Pt(size)
        p.space_after = Pt(6)


def add_picture(slide, path, left, top, width=None, height=None):
    path = Path(path)
    if not path.exists():
        print(f"  [warn] missing image: {path}")
        return None
    if width and height:
        return slide.shapes.add_picture(str(path), left, top, width=width, height=height)
    if width:
        return slide.shapes.add_picture(str(path), left, top, width=width)
    if height:
        return slide.shapes.add_picture(str(path), left, top, height=height)
    return slide.shapes.add_picture(str(path), left, top)


def ensure_assets():
    ASSETS.mkdir(parents=True, exist_ok=True)
    # charts
    subprocess.run([sys.executable, str(ASSETS / "make_charts.py")], check=True, cwd=ROOT)
    # normE slices if not present
    mesh = ROOT / "scat3D/Release/SBCmesh.dat"
    outdir = ROOT / "scat3D/Release/OutFile"
    plot_script = ROOT / "tools/plot_normE_slice.py"
    if mesh.exists() and (outdir / "normE").exists() and plot_script.exists():
        for plane, coord, name in [("z", 0.5, "normE_z050.png"), ("z", 0, "normE_z000.png"), ("y", 0.5, "normE_y050.png")]:
            out = ASSETS / name
            if not out.exists():
                subprocess.run([
                    sys.executable, str(plot_script),
                    "--mesh", str(mesh), "--outdir", str(outdir),
                    "--plane", plane, "--coord", str(coord),
                    "--output", str(out),
                ], cwd=ROOT, check=False)


def build(prs):
    delete_all_slides(prs)
    # emu helpers — reference deck is 13.33" wide
    from pptx.util import Inches
    W = prs.slide_width

    # --- 1 封面（仿参考第1页布局）---
    slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
    set_title(slide, "组会汇报")
    add_textbox(
        slide, Inches(0), Inches(2.9), W, Inches(1.6),
        "OpticsFEM 二维→三维扩展工作总结\n"
        "一阶 Nedelec 边元 · 散射边界条件与本征频率问题\n"
        "汇报人：        时间：2026年6月",
        size=22, bold=False,
    )
    add_picture(slide, REF_IMGS / "image5.png", Inches(7.2), Inches(1.5), width=Inches(5.5))

    # --- 2 背景与目标 ---
    slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
    set_title(slide, "项目背景与目标")
    add_bullets(slide, Inches(0.35), Inches(1.35), Inches(5.8), Inches(5.5), [
        "原有 OpticsFEM：2D 三角网格 + 一阶边元（FemType 0–3）",
        "目标：扩展至 3D 四面体网格，对标 MATLAB 参考代码",
        "算例：SBC 立方体模型，εr=1.0/1.5，λ₀=0.8 m",
        "散射：assembly_equ + out + inc → Ax=b",
        "本征：assembly_equ → A·x=λ·B·x",
        "交付：C++ 散射全流程 + 本征矩阵与 MATLAB 对齐",
    ])
    add_picture(slide, REF_IMGS / "image6.png", Inches(6.3), Inches(1.6), width=Inches(6.5))

    # --- 3 SBC 物理（仿参考“散射边界条件模型”）---
    slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
    set_title(slide, "散射边界条件（SBC）物理模型")
    add_textbox(slide, Inches(0.35), Inches(1.8), Inches(5.5), Inches(4.5),
                "散射边界条件（Scattering Boundary Condition）\n\n"
                "• 出射边界：模拟只有沿外法向传播的出射波\n"
                "• 入射激励：平面波 Einc=(1,0,0)\n"
                "• 体积分：curl-curl − k₀²ε\n"
                "• C++ 实现：Assemble_SBC + assembly_inc/out\n"
                "• 与 MATLAB main.m 流程一致", size=14)
    add_picture(slide, REF_IMGS / "image8.png", Inches(1.5), Inches(4.8), width=Inches(3.5))
    add_picture(slide, REF_IMGS / "image17.png", Inches(6.0), Inches(1.5), width=Inches(6.8))

    # --- 4 2D vs 3D ---
    slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
    set_title(slide, "二维与三维扩展对照")
    add_picture(slide, ASSETS / "status_table.png", Inches(0.4), Inches(1.4), width=Inches(12.0))
    add_textbox(slide, Inches(0.5), Inches(5.0), Inches(12), Inches(1.2),
                "新增 FemType 4（3D 散射）与 5（3D 本征）；共用 Mesh_3D + SBCmesh.dat",
                size=13)

    # --- 5 软件架构 ---
    slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
    set_title(slide, "软件整体架构")
    add_picture(slide, ASSETS / "workflow.png", Inches(0.5), Inches(1.35), width=Inches(12.2))
    add_bullets(slide, Inches(0.5), Inches(5.2), Inches(12), Inches(1.5), [
        "模块：mesh · material · phy · kernel · solver · post · Interface",
        "构建：scat3D/（散射）与 eigen3D/（本征）独立 CMake 目录",
    ], size=12)

    # --- 6 散射方程与流程（仿参考“方程”页）---
    slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
    set_title(slide, "三维散射问题（FemType = 4）")
    add_picture(slide, REF_IMGS / "image3.png", Inches(0.4), Inches(1.5), width=Inches(5.5))
    add_picture(slide, REF_IMGS / "image4.png", Inches(6.2), Inches(1.5), width=Inches(6.5))
    add_textbox(slide, Inches(0.5), Inches(5.5), Inches(12), Inches(1.2),
                "方程 A·x=b  |  配置 sbc3d.json  |  返回码 4  |  complexsolver SparseLU",
                size=13, bold=True)

    # --- 7 散射数值结果 ---
    slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
    set_title(slide, "散射仿真结果 |E| 分布")
    add_picture(slide, ASSETS / "normE_z050.png", Inches(0.4), Inches(1.35), width=Inches(6.0))
    add_picture(slide, ASSETS / "metrics.png", Inches(6.6), Inches(1.35), width=Inches(6.2))
    add_textbox(slide, Inches(0.5), Inches(5.6), Inches(12), Inches(0.8),
                "z=0.5 m 切面  |  |E| min=0.72 max=1.36 V/m  |  残差 |r|≈6.2×10⁻¹³",
                size=13)

    # --- 8 多切面对比 ---
    slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
    set_title(slide, "散射场多切面 |E|（C++ Post 输出）")
    add_picture(slide, ASSETS / "normE_z000.png", Inches(0.3), Inches(1.4), width=Inches(4.0))
    add_picture(slide, ASSETS / "normE_z050.png", Inches(4.5), Inches(1.4), width=Inches(4.0))
    add_picture(slide, ASSETS / "normE_y050.png", Inches(8.7), Inches(1.4), width=Inches(4.0))
    add_textbox(slide, Inches(0.3), Inches(1.2), Inches(4), Inches(0.3), "z = 0", size=11, bold=True)
    add_textbox(slide, Inches(4.5), Inches(1.2), Inches(4), Inches(0.3), "z = 0.5 m", size=11, bold=True)
    add_textbox(slide, Inches(8.7), Inches(1.2), Inches(4), Inches(0.3), "y = 0.5 m", size=11, bold=True)
    add_textbox(slide, Inches(0.5), Inches(5.8), Inches(12), Inches(0.6),
                "可与 COMSOL 切面对比：compare/compare_normE_zslice.m", size=12)

    # --- 9 本征问题 ---
    slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
    set_title(slide, "三维本征频率问题（FemType = 5）")
    add_bullets(slide, Inches(0.35), Inches(1.4), Inches(5.5), Inches(5.5), [
        "广义本征：A·x = λ·B·x",
        "A = curl-curl（刚度），B = ε 质量矩阵",
        "边界：全 PMC（与 MATLAB 3D一阶本征问题2 一致）",
        "C++：Assemble + Test_OutputMatrix",
        "导出：Ai/Aj/Av、Bi/Bj/Bv（COO，0-based）",
        "求解：MATLAB eigs(A,B) + get_ele 绘图",
        "待接：C++ Run/Post",
    ])
    add_picture(slide, REF_IMGS / "image27.png", Inches(6.0), Inches(1.5), width=Inches(6.8))

    # --- 10 本征矩阵验证 ---
    slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
    set_title(slide, "本征矩阵组装验证（C++ vs MATLAB）")
    add_picture(slide, ASSETS / "metrics.png", Inches(0.4), Inches(1.4), width=Inches(6.5))
    add_bullets(slide, Inches(6.8), Inches(1.5), Inches(6), Inches(5), [
        "Av.txt 约 1.29×10⁶ 行非零元",
        "C++ OutFile 与 MATLAB OutFile 逐行一致",
        "Bi/Bj/Bv 约 43632 非零（质量矩阵）",
        "FemType=5 返回码：5",
        "下一步：接入 Solver_EigenFreq",
    ], size=14)

    # --- 11 求解器 ---
    slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
    set_title(slide, "求解器体系")
    add_picture(slide, REF_IMGS / "image12.png", Inches(0.4), Inches(1.5), width=Inches(5.5))
    add_bullets(slide, Inches(6.2), Inches(1.5), Inches(6.5), Inches(5), [
        "solver/：接口层（编进 OpticsFEM.exe）",
        "散射：interface.cpp → complexsolver.exe",
        "complexsolver：Eigen SparseLU / BiCGSTAB",
        "路径：Release/complex/complexsolver.exe",
        "本征 3D：当前 MATLAB eigs 验证",
        "优势：无需 PETSc，CMake 一键编译",
    ], size=13)

    # --- 12 总结 ---
    slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
    set_title(slide, "总结与下一步")
    add_bullets(slide, Inches(0.4), Inches(1.4), Inches(6), Inches(5.5), [
        "【已完成】2D→3D 一阶 Nedelec 框架扩展",
        "【已完成】3D SBC 散射端到端可运行、数值可靠",
        "【已完成】3D 本征 A/B 组装与 MATLAB 严格对齐",
        "【待完成】本征 C++ 求解与后处理接入",
        "【待完成】PBC、二阶基等扩展（MATLAB 已有）",
        "【待完成】Test_Main 支持命令行指定 JSON",
    ], size=14)
    add_picture(slide, ASSETS / "normE_z050.png", Inches(6.5), Inches(1.5), width=Inches(6.0))

    # --- 13 致谢 ---
    slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_TITLE_CONTENT])
    set_title(slide, "下一步工作")
    add_textbox(
        slide, Inches(1.5), Inches(2.5), Inches(10), Inches(2.5),
        "1. COMSOL 数值对比出图（compare 脚本）\n"
        "2. FemType=5 接入 Run/Post，完成 C++ 本征闭环\n"
        "3. 扩展 PBC / 二阶基边界条件\n"
        "4. 算例自动化与文档完善\n\n"
        "谢谢！",
        size=22, bold=False,
    )


def main():
    print("Preparing assets...")
    ensure_assets()
    if not REF_PPT.exists():
        raise FileNotFoundError(f"Reference PPT not found: {REF_PPT}")
    print(f"Loading template: {REF_PPT}")
    prs = Presentation(str(REF_PPT))
    build(prs)
    prs.save(str(OUT_PATH))
    print(f"Saved: {OUT_PATH}")


if __name__ == "__main__":
    main()
