#!/usr/bin/env python3
"""build_wintershall_template.py — Generieke Wintershall master-template bouwen.

Basis: deliverables/Wintershall_Training_Stof_v1.0.pptx (goedgekeurd design,
32 slides). Reduceert tot 10 placeholder-slides; master/layouts/logo's en
kleurpalet (#005493 / #FF9900 / #F0F8FF) blijven onaangetast.

Regels (MASTER_STYLEGUIDE §3.5):
- interne links uitsluitend slide-rel + action="ppaction://hlinksldjump"
- geen 'slide://' hyperlinks
- WINTERSHALL-REGEL: 0x JvG (geen logo, footer of tekst)
- sldId's netjes hernummerd (256..265) na reductie

Run: /root/.hermes/venv/bin/python scripts/build_wintershall_template.py
"""
from pathlib import Path

from pptx import Presentation
from pptx.dml.color import RGBColor
from pptx.enum.shapes import MSO_SHAPE
from pptx.enum.text import PP_ALIGN
from pptx.opc.constants import RELATIONSHIP_TYPE as RT
from pptx.oxml.ns import qn
from pptx.util import Inches, Pt

PROJ = Path(__file__).resolve().parent.parent
SRC = PROJ / "deliverables/Wintershall_Training_Stof_v1.0.pptx"
DST = PROJ / "deliverables/Wintershall_Training_Master_v1.0.pptx"

PRIMARY = "005493"
ORANGE = "FF9900"
PANEL = "F0F8FF"
WHITE = "FFFFFF"
BODY = "333333"
FONT = "Arial"

# --------------------------------------------------------------------------
# Nieuwe deckvolgorde (oorspronkelijke slideN.xml onderdelen)
KEEP_ORDER = [1, 2, 3, 11, 5, 12, 30, 18, 22, 32]
#            titel, menu, leerdoelen, kaarten, wetgeving, piramide,
#            tabel, RASCI, quiz, kernpunten

# Sidebar-menu: 8 labels in documentvolgorde -> (nieuw label, doelslide)
NAV = [
    ("Leerdoelen", 3),
    ("Content", 11),
    ("Wetgeving", 5),
    ("Diagram", 12),
    ("Tabel", 30),
    ("RASCI", 18),
    ("Quiz", 22),
    ("Kernpunten", 32),
]
JUMP_ACTION = "ppaction://hlinksldjump"


def EMU(v):
    return int(v)


def set_box_text(shape, new_text):
    """Tekst vervangen, eerste run-formatering behouden (ook '• x' 2-run patroon)."""
    tf = shape.text_frame
    paras = tf.paragraphs
    p0 = paras[0]
    runs = p0.runs
    if not runs:
        p0.add_run().text = new_text
        return
    if len(runs) >= 2 and runs[0].text.strip() in ("•", "✓"):
        runs[1].text = new_text
        for r in runs[2:]:
            r._r.getparent().remove(r._r)
    else:
        runs[0].text = new_text
        for r in runs[1:]:
            r._r.getparent().remove(r._r)
    for p in paras[1:]:
        p._p.getparent().remove(p._p)


def classify(slide):
    """Scheid titel / subtitel / content-boxen / sidebar (per geometrie)."""
    title = sub = None
    bullets = []
    for sh in slide.shapes:
        if not sh.has_text_frame or not sh.shape_type == 17:  # TEXT_BOX
            continue
        if sh.width is None or sh.height is None:
            continue
        w_in, l_in, t_in = sh.width / 914400, sh.left / 914400, sh.top / 914400
        txt = sh.text_frame.text.strip()
        if not txt and w_in < 3:
            continue
        if w_in < 1.0 and l_in < 0.4:      # sidebar-labels
            continue
        if t_in < 0.7 and w_in >= 3:
            title = sh
        elif 0.7 <= t_in < 1.15 and w_in >= 3:
            sub = sh
        elif t_in >= 1.15 and w_in >= 3 and l_in >= 0.7:
            bullets.append(sh)
    bullets.sort(key=lambda s: (s.top, s.left))
    return title, sub, bullets


def apply_content(slide, title_txt, sub_txt, bullet_txts):
    title, sub, bullets = classify(slide)
    if title is not None and title_txt is not None:
        set_box_text(title, title_txt)
    if sub is not None and sub_txt is not None:
        set_box_text(sub, sub_txt)
    for i, sh in enumerate(bullets):
        if i < len(bullet_txts):
            set_box_text(sh, bullet_txts[i])
        else:
            sh._element.getparent().remove(sh._element)


def add_textbox(slide, x, y, w, h, text, size, color, bold=False,
                align=PP_ALIGN.LEFT, font=FONT):
    box = slide.shapes.add_textbox(x, y, w, h)
    tf = box.text_frame
    tf.word_wrap = True
    p = tf.paragraphs[0]
    p.alignment = align
    r = p.add_run()
    r.text = text
    r.font.name = font
    r.font.size = Pt(size)
    r.font.bold = bold
    r.font.color.rgb = RGBColor.from_string(color)
    return box


def add_panel(slide, x, y, w, h):
    shp = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, w, h)
    shp.fill.solid()
    shp.fill.fore_color.rgb = RGBColor.from_string(PANEL)
    shp.line.color.rgb = RGBColor.from_string(PRIMARY)
    shp.line.width = Pt(0.75)
    shp.shadow.inherit = False
    return shp


def build():
    prs = Presentation(str(SRC))
    sldIdLst = prs.slides._sldIdLst

    # map partname -> slide-object
    by_part = {}
    for s in prs.slides:
        by_part[str(s.part.partname)] = s

    # ---- 1) overtollige slides verwijderen -------------------------------
    keep_parts = {f"/ppt/slides/slide{n}.xml" for n in KEEP_ORDER}
    for sldId in list(sldIdLst):
        rId = sldId.get(qn("r:id"))
        part = prs.part.rels[rId].target_part
        if str(part.partname) not in keep_parts:
            prs.part.drop_rel(rId)
            sldIdLst.remove(sldId)

    # ---- 2) volgorde + nette sldId-hernummering --------------------------
    id_by_part = {f"/ppt/slides/slide{n}.xml": by_part[f"/ppt/slides/slide{n}.xml"].part
                   for n in KEEP_ORDER}
    sldId_by_part = {}
    for sldId in list(sldIdLst):
        rId = sldId.get(qn("r:id"))
        part = prs.part.rels[rId].target_part
        sldId_by_part[str(part.partname)] = sldId
    for i, n in enumerate(KEEP_ORDER):
        el = sldId_by_part[f"/ppt/slides/slide{n}.xml"]
        sldIdLst.remove(el)
        el.set("id", str(256 + i))
        sldIdLst.append(el)

    # ---- 3) navigatie-jumps herbestemmen + sidebar-labels -----------------
    for n in KEEP_ORDER:
        slide = by_part[f"/ppt/slides/slide{n}.xml"]
        part = slide.part
        nav_shapes = []
        for sh in slide.shapes:
            cNvPr = sh._element.find(
                ".//" + qn("p:cNvPr"))
            hl = cNvPr.find(qn("a:hlinkClick")) if cNvPr is not None else None
            if hl is not None and hl.get("action") == JUMP_ACTION:
                nav_shapes.append((sh, hl))
        # Eerst alle oude jump-rels laten vallen (relate_to hergebruikt
        # anders bestaande rels naar hetzelfde target -> aliasing).
        for sh, hl in nav_shapes:
            old_rId = hl.get(qn("r:id"))
            if old_rId in part.rels:
                part.drop_rel(old_rId)
        # Daarna verse rels aanmaken en hlink + label zetten.
        for (sh, hl), (label, target_n) in zip(nav_shapes, NAV):
            new_rId = part.relate_to(
                id_by_part[f"/ppt/slides/slide{target_n}.xml"], RT.SLIDE)
            hl.set(qn("r:id"), new_rId)
            set_box_text(sh, label)

    # ---- 4) placeholder-content per slide ---------------------------------
    S = {n: by_part[f"/ppt/slides/slide{n}.xml"] for n in KEEP_ORDER}

    # (1) Titelfolie
    s1 = S[1]
    add_textbox(s1, Inches(1.0), Inches(4.32), Inches(8.0), Inches(0.45),
                "Ondertitel | Wintershall", 18, PRIMARY, bold=True,
                align=PP_ALIGN.CENTER)

    # (2) Welkom / menu
    apply_content(S[2], "Welkom bij de training",
                  "Menu | Klik op een sectie in het menu links", [
        "Deze slide gebruik je als welkom én navigatiemenu",
        "Het menu links springt naar elke sectie van de training",
        "Vervang de placeholderteksten door je eigen inhoud",
        "Verwijder deze instructies vóór gebruik",
    ])

    # (3) Leerdoelen
    apply_content(S[3], "Leerdoelen", "Wat kan de deelnemer na deze training?", [
        "Leerdoel 1 — [vul in]",
        "Leerdoel 2 — [vul in]",
        "Leerdoel 3 — [vul in]",
        "Leerdoel 4 — [vul in]",
        "Leerdoel 5 — [vul in]",
    ])

    # (4) Content in 2-koloms kaartjes
    s4 = S[11]
    apply_content(s4, "Content in kaarten",
                  "Twee kolommen met kernpunten", [])
    for col, (x, head) in enumerate([
            (Inches(1.0), "Kaart 1 — kop"), (Inches(5.4), "Kaart 2 — kop")]):
        add_panel(s4, x, Inches(1.35), Inches(4.0), Inches(3.4))
        add_textbox(s4, x + Inches(0.25), Inches(1.55), Inches(3.5),
                    Inches(0.4), head, 15, PRIMARY, bold=True)
        for j in range(4):
            add_textbox(s4, x + Inches(0.25), Inches(2.1) + Inches(0.55) * j,
                        Inches(3.5), Inches(0.5),
                        "• Placeholder punt " + str(j + 1), 12, BODY)
        add_textbox(s4, x + Inches(0.25), Inches(4.35), Inches(3.5),
                    Inches(0.3), "[vul kaarttekst in]", 10, PRIMARY)

    # (5) Wetgeving & bronnen
    apply_content(S[5], "Wetgeving & bronnen",
                  "Relevant kader en referenties", [
        "Wetgeving 1 — [vul in]",
        "Wetgeving 2 — [vul in]",
        "Norm of standaard — [vul in]",
        "Intern document — [vul in]",
        "Bron of referentie — [vul in]",
    ])

    # (6) Piramide / diagram-placeholder
    s6 = S[12]
    apply_content(s6, "Diagram-placeholder", None, [])
    trapz = [sh for sh in s6.shapes
             if sh.shape_type == 1 and sh.has_text_frame
             and sh.text_frame.text.strip()]
    trapz.sort(key=lambda s: s.top)
    niveaus = [("4. Niveau 4", "Placeholder laag 4"),
               ("3. Niveau 3", "Placeholder laag 3"),
               ("2. Niveau 2", "Placeholder laag 2"),
               ("1. Niveau 1", "Placeholder laag 1")]
    for sh, (l1, l2) in zip(trapz, niveaus):
        paras = sh.text_frame.paragraphs
        if paras and paras[0].runs:
            paras[0].runs[0].text = l1
            for r in paras[0].runs[1:]:
                r._r.getparent().remove(r._r)
        if len(paras) > 1 and paras[1].runs:
            paras[1].runs[0].text = l2
            for r in paras[1].runs[1:]:
                r._r.getparent().remove(r._r)
        for p in paras[2:]:
            p._p.getparent().remove(p._p)

    # (7) Tabel-placeholder
    s7 = S[30]
    apply_content(s7, "Tabel-placeholder", None, [])
    rows, cols = 4, 3
    tbl = s7.shapes.add_table(rows, cols, Inches(1.0), Inches(1.35),
                              Inches(8.4), Inches(2.6)).table
    headers = ["Kolom 1", "Kolom 2", "Kolom 3"]
    for c, htxt in enumerate(headers):
        cell = tbl.cell(0, c)
        cell.fill.solid()
        cell.fill.fore_color.rgb = RGBColor.from_string(PRIMARY)
        cell.text = htxt
        for p in cell.text_frame.paragraphs:
            for r in p.runs:
                r.font.name = FONT
                r.font.size = Pt(12)
                r.font.bold = True
                r.font.color.rgb = RGBColor.from_string(WHITE)
    for ri in range(1, rows):
        for ci in range(cols):
            cell = tbl.cell(ri, ci)
            cell.fill.solid()
            cell.fill.fore_color.rgb = RGBColor.from_string(
                WHITE if ri % 2 else PANEL)
            cell.text = f"Rij {ri} — [vul in]" if ci == 0 else "[vul in]"
            for p in cell.text_frame.paragraphs:
                for r in p.runs:
                    r.font.name = FONT
                    r.font.size = Pt(11)
                    r.font.color.rgb = RGBColor.from_string(BODY)

    # (8) RASCI
    apply_content(S[18], "RASCI-model", "Rollen en verantwoordelijkheden", [
        "R = Responsible — uitvoering [vul in]",
        "A = Accountable — eindverantwoordelijk [vul in]",
        "S = Supportive — ondersteunend [vul in]",
        "C = Consulted — raadplegen [vul in]",
        "I = Informed — informeren [vul in]",
        "Vul per taak de juiste rol in",
    ])

    # (9) Quiz
    s9 = S[22]
    apply_content(s9, "Quiz", "Toets de kennis", [
        "Vraag: [vul je vraag in]",
        "Antwoord A — [optie]",
        "Antwoord B — [optie]",
        "Antwoord C — [optie]",
        "Bespreek het juiste antwoord en de onderbouwing",
    ])
    chips = [sh for sh in s9.shapes if sh.has_text_frame
             and sh.shape_type == 17 and sh.width < Inches(2.0)
             and sh.left > Inches(0.5)
             and sh.top > Inches(4.0) and sh.text_frame.text.strip()]
    chips.sort(key=lambda s: s.left)
    for sh, lbl in zip(chips, ["Optie A", "Optie B", "Optie C", "Juist"]):
        set_box_text(sh, lbl)

    # (10) Kernpunten
    apply_content(S[32], "Kernpunten — samenvatting", None, [
        "Kernpunt 1 — [vul in]",
        "Kernpunt 2 — [vul in]",
        "Kernpunt 3 — [vul in]",
        "Kernpunt 4 — [vul in]",
        "Kernpunt 5 — [vul in]",
        "Kernpunt 6 — [vul in]",
        "Kernpunt 7 — [vul in]",
        "Kernpunt 8 — [vul in]",
    ])

    prs.core_properties.title = "Wintershall Training Master v1.0"
    prs.save(str(DST))
    print(f"OK: {DST} ({DST.stat().st_size} bytes)")


if __name__ == "__main__":
    build()
