# SPDX-License-Identifier: Apache-2.0
"""Generate eight original illustrated A4 HASS W23–24 pages and alternatives."""

from __future__ import annotations

import argparse
import os
import subprocess
from html import escape
from pathlib import Path

HERE = Path(__file__).resolve().parent
INK = "#173d4f"
BLUE = "#e2f1f8"
GREEN = "#e2f3e8"
SAND = "#fff0db"
LILAC = "#f1e9f8"


def box(x, y, w, h, fill="white", radius=13, width=3):
    return f'<rect x="{x}" y="{y}" width="{w}" height="{h}" rx="{radius}" fill="{fill}" stroke="{INK}" stroke-width="{width}"/>'


def word(x, y, value, size=18, bold=False):
    return f'<text x="{x}" y="{y}" font-family="DejaVu Sans" font-size="{size}" font-weight="{700 if bold else 400}" fill="{INK}">{escape(value)}</text>'


def stroke(d, width=4):
    return f'<path d="{d}" fill="none" stroke="{INK}" stroke-width="{width}" stroke-linecap="round" stroke-linejoin="round"/>'


def circle(cx, cy, r, fill="white"):
    return f'<circle cx="{cx}" cy="{cy}" r="{r}" fill="{fill}" stroke="{INK}" stroke-width="3"/>'


def polygon(points, fill="white"):
    return f'<polygon points="{points}" fill="{fill}" stroke="{INK}" stroke-width="3"/>'


def words(items, x, y, step=39, size=17):
    return "".join(word(x, y + n * step, item, size) for n, item in enumerate(items))


def page(title, desc, body):
    return "\n".join(
        [
            '<svg xmlns="http://www.w3.org/2000/svg" width="210mm" height="297mm" viewBox="0 0 794 1123" role="img" aria-labelledby="title desc">',
            f'<title id="title">{escape(title)}</title>',
            f'<desc id="desc">{escape(desc)}</desc>',
            '<rect width="794" height="1123" fill="white"/>',
            box(29, 30, 736, 1063),
            word(46, 74, "SUBJECTNEST · FOUNDATION HASS · W23–24", 17, True),
            word(46, 115, title, 25, True),
            word(46, 148, desc, 14),
            body,
            stroke("M46 1041 L748 1041", 2),
            word(
                46,
                1070,
                "Original fictional source · CC BY 4.0 · exact alternatives in pack",
                11,
            ),
            "</svg>",
            "",
        ]
    )


def window(x, y, w=210, h=200):
    b = box(x, y, w, h, "white", 3, 5)
    b += stroke(
        f"M{x + w / 2} {y} L{x + w / 2} {y + h} M{x} {y + h / 2} L{x + w} {y + h / 2}",
        4,
    )
    b += stroke(f"M{x - 28} {y + h + 7} L{x + w + 28} {y + h + 7}", 5)
    return b


def strip(x, y, fill=GREEN):
    return box(x, y, 58, 138, fill, 2, 3) + circle(x + 29, y + 23, 6, "white")


def book(x, y):
    return (
        box(x, y, 125, 89, "white", 2)
        + stroke(f"M{x + 62} {y} L{x + 62} {y + 89}", 3)
        + stroke(
            f"M{x + 20} {y + 23} L{x + 46} {y + 23} M{x + 79} {y + 23} L{x + 105} {y + 23}",
            2,
        )
    )


def gate(x, y):
    return stroke(
        f"M{x} {y + 180} L{x} {y + 60} Q{x + 110} {y - 50} {x + 220} {y + 60} L{x + 220} {y + 180}",
        9,
    ) + stroke(
        f"M{x + 35} {y + 180} L{x + 35} {y + 75} M{x + 185} {y + 180} L{x + 185} {y + 75}",
        4,
    )


def board(x, y):
    return box(x, y, 229, 138, "white", 4, 5) + stroke(
        f"M{x + 26} {y + 138} L{x + 16} {y + 186} M{x + 203} {y + 138} L{x + 213} {y + 186}",
        6,
    )


def notice():
    b = word(48, 194, "SOURCE A · PAPER ROOM NOTICE", 19, True)
    b += box(58, 219, 678, 493, BLUE) + window(292, 265, 210, 190)
    for x, fill in ((233, GREEN), (368, SAND), (503, LILAC)):
        b += strip(x, 520, fill)
    b += word(77, 683, "WINDOW + THREE DRAWN PAPER STRIPS", 15, True)
    b += words(
        [
            "“Our pretend Paper Room opened a new reading",
            "window on Thursday. On Friday, some visitors",
            "made paper strips to remember the opening.",
            "This notice does not list every visitor or say",
            "why each person did or did not join.”",
        ],
        65,
        768,
        43,
        17,
    )
    return page(
        "Paper Room · opening notice",
        "Invented event and marking, not an attendance record.",
        b,
    )


def alba():
    b = word(48, 194, "SOURCE B · ALBA'S PERSONAL NOTE", 19, True)
    b += box(59, 222, 678, 714, GREEN) + strip(359, 278, SAND)
    b += circle(388, 264, 17, "white")
    b += words(
        [
            "“I made a paper strip because the new window",
            "matters to me. I like to read beside the light.”",
        ],
        78,
        545,
        47,
        19,
    )
    b += box(85, 694, 624, 124, "white")
    b += word(106, 742, "ONE PERSON'S REASON · ALBA ONLY", 20, True)
    b += word(106, 782, "It does not explain another person's choice.", 16)
    return page(
        "Alba · individual reason", "A made-up strip maker speaks only for themself.", b
    )


def jun():
    b = word(48, 194, "SOURCE C · JUN'S PERSONAL NOTE", 19, True)
    b += box(59, 222, 678, 714, LILAC) + window(110, 282, 161, 154) + book(454, 365)
    b += words(
        [
            "“I visited on Saturday and read beside the",
            "window. I did not make a paper strip.",
            "I have not said why I skipped it.”",
        ],
        77,
        569,
        46,
        19,
    )
    b += box(87, 745, 619, 107, "white")
    b += word(107, 795, "ACTION IS STATED · REASON NOT TOLD", 18, True)
    b += word(83, 900, "Do not guess a feeling or motive from silence.", 16)
    return page(
        "Jun · action, reason open", "A made-up visitor may keep a reason private.", b
    )


def questions():
    b = word(48, 194, "SOURCE D · COMPARE FOUR QUESTION CARDS", 18, True)
    items = [
        ("1 · ASK WITHOUT ASSUMING", "Did you make a strip?", BLUE),
        (
            "2 · INVITE; ALLOW PASS",
            "Would you like to tell me what the opening meant to you? You may pass.",
            GREEN,
        ),
        ("3 · ASSUMES EVERYONE", "Why did everyone celebrate?", SAND),
        (
            "4 · ASSUMES A MOTIVE",
            "You did not make a strip, so you did not care?",
            LILAC,
        ),
    ]
    for i, (head, question, fill) in enumerate(items):
        y = 225 + i * 185
        b += box(62, y, 670, 158, fill) + circle(112, y + 77, 27, "white")
        b += word(103, y + 85, str(i + 1), 22, True)
        b += word(167, y + 55, head, 18, True)
        if i == 1:
            b += word(167, y + 94, "Would you like to tell me what the opening", 15)
            b += word(167, y + 124, "meant to you? You may pass.", 15)
        else:
            b += word(167, y + 108, question, 16)
    b += word(
        62, 1006, "No real person must answer; a private reason may stay open.", 15
    )
    return page(
        "Respectful question cards",
        "Some questions invite; others assume an unproved answer.",
        b,
    )


def some_mat():
    b = word(48, 194, "WHAT DOES THE SOURCE SAY ABOUT PEOPLE?", 17, True)
    items = [
        ("SOME", "A names some strip-makers, not everyone.", GREEN),
        ("NAMED PERSON", "Alba gives Alba's own reason.", BLUE),
        ("NOT ALL", "Jun reports not making a strip.", SAND),
        ("NOT TOLD", "Other people and Jun's reason remain open.", LILAC),
    ]
    for i, (head, prompt, fill) in enumerate(items):
        y = 225 + i * 185
        b += box(62, y, 670, 158, fill)
        b += circle(112, y + 78, 28, "white") + word(97, y + 87, "?", 28, True)
        b += word(165, y + 59, head, 19, True) + word(165, y + 106, prompt, 16)
    b += word(63, 1007, "Drawn strips are not an attendance count.", 15)
    return page(
        "Some, not all, not told",
        "Four boxes prevent a picture from becoming a head count.",
        b,
    )


def voices_mat():
    b = word(48, 194, "TELL A SOURCE-BOUNDED TWO-VOICE ACCOUNT", 17, True)
    items = [
        ("A · NOTICE", "Opening and some marking are stated.", BLUE),
        ("B · ALBA", "Alba says why Alba made a strip.", GREEN),
        ("C · JUN", "Jun says they read but did not make one.", SAND),
        ("OPEN QUESTION", "Jun has not said why. Do not fill this gap.", LILAC),
    ]
    for i, (head, prompt, fill) in enumerate(items):
        y = 226 + i * 183
        b += box(61, y, 672, 156, fill)
        b += word(84, y + 48, head, 19, True) + word(84, y + 91, prompt, 16)
        b += stroke(f"M84 {y + 128} L706 {y + 128}", 2)
    b += word(63, 1006, "A source can be silent. You may leave a question open.", 15)
    return page(
        "Two-voice account mat", "Named reasons, actions and unknowns stay separate.", b
    )


def check_a():
    b = word(48, 194, "FRESH CHECK A · PAPER GATE DAY", 18, True)
    b += box(59, 219, 678, 347, BLUE)
    b += '<g transform="translate(311 259) scale(0.75)">' + gate(0, 0) + "</g>"
    b += strip(147, 280, GREEN) + strip(577, 280, SAND)
    b += word(77, 251, "SOURCE 1 · KEEPER'S NOTICE", 16, True)
    b += words(
        [
            "“Our pretend Paper Gate opened on Tuesday. On",
            "Wednesday, some visitors made paper tags to mark",
            "the opening. The notice does not name all visitors",
            "or tell why each person joined.”",
        ],
        78,
        461,
        27,
        14,
    )
    b += box(59, 585, 678, 168, GREEN)
    b += words(
        [
            "SOURCE 2 · YARA",
            "“I made one paper tag because the opening",
            "mattered to me. I can now use the drawing table",
            "beyond the gate.”",
        ],
        77,
        620,
        34,
        15,
    )
    b += box(59, 771, 678, 227, LILAC)
    b += words(
        [
            "SOURCE 3 · TEO",
            "“I visited on Thursday. I did not make a paper tag.",
            "I have not told you why I skipped it.”",
            "Teo's action is stated; Teo's motive is not.",
        ],
        77,
        811,
        41,
        16,
    )
    return page(
        "Check A · Paper Gate Day",
        "Fresh fictional opening, one maker and one non-maker.",
        b,
    )


def check_b():
    b = word(48, 194, "FRESH CHECK B · CHALK BOARD OPENING", 17, True)
    b += box(59, 218, 678, 275, BLUE)
    b += word(77, 249, "SOURCE 1 · ROOM NOTICE", 15, True)
    b += '<g transform="translate(321 235) scale(0.75)">' + board(0, 0) + "</g>"
    b += polygon("119,284 94,344 144,344", SAND) + circle(650, 316, 25, GREEN)
    b += words(
        [
            "“Our pretend room opened a new chalk board on",
            "Monday. On Friday, some visitors put paper shapes",
            "nearby to remember the opening. This notice does not",
            "say who all the visitors were or how they felt.”",
        ],
        78,
        399,
        24,
        13,
    )
    b += box(59, 511, 678, 130, GREEN)
    b += words(
        [
            "SOURCE 2 · REMI",
            "“I put up a triangle because I helped carry the",
            "new board. I wanted to remember that day.”",
        ],
        77,
        542,
        33,
        15,
    )
    b += box(59, 658, 678, 137, SAND)
    b += words(
        [
            "SOURCE 3 · SOL",
            "“I looked at the board on Saturday. I did not",
            "put up a shape. I have not said why.”",
        ],
        77,
        689,
        34,
        15,
    )
    b += box(59, 813, 678, 187, LILAC)
    b += words(
        [
            "SOURCE 4 · TWO DRAFT QUESTIONS",
            "A · Why did everybody celebrate with shapes?",
            "B · Did you put up a shape? If you want, what did",
            "the opening mean to you? You may pass.",
        ],
        77,
        848,
        37,
        15,
    )
    return page(
        "Check B · Chalk Board",
        "Fresh event, mixed participation and two questions.",
        b,
    )


PAGES = {
    "paper-window-notice": notice,
    "alba-voice": alba,
    "jun-voice": jun,
    "respectful-question-cards": questions,
    "some-not-all-mat": some_mat,
    "two-voice-account-mat": voices_mat,
    "check-a-paper-gate": check_a,
    "check-b-chalk-board": check_b,
}

ALTS = {
    "paper-window-notice": "Source A, fictional Paper Room notice. Illustration: one four-pane rectangular reading window above three separate hanging paper strips; these are not a count of people. Exact words: ‘Our pretend Paper Room opened a new reading window on Thursday. On Friday, some visitors made paper strips to remember the opening. This notice does not list every visitor or say why each person did or did not join.’ Tactile/no-print: use one rectangle split into four and three loose strips; read the exact notice, then leave an ALL VISITORS card blank.",
    "alba-voice": "Source B, Alba's invented personal note beside one hanging strip. Exact words: ‘I made a paper strip because the new window matters to me. I like to read beside the light.’ The page says ONE PERSON'S REASON · ALBA ONLY. Tactile/no-print: use one Alba name token beside one strip, read exact words, and leave OTHER REASONS empty.",
    "jun-voice": "Source C, Jun's invented personal note. Illustration: a reading window and an open book; no strip appears in Jun's panel. Exact words: ‘I visited on Saturday and read beside the window. I did not make a paper strip. I have not said why I skipped it.’ Action is stated; reason is not told. Tactile/no-print: move VISITED, READ and DID NOT MAKE tokens; leave REASON blank.",
    "respectful-question-cards": "Source D, four numbered cards. 1 ‘Did you make a strip?’ asks without assuming. 2 ‘Would you like to tell me what the opening meant to you? You may pass.’ invites and allows a pass. 3 ‘Why did everyone celebrate?’ assumes everybody joined. 4 ‘You did not make a strip, so you did not care?’ assumes an unreported motive. No real person must answer. Tactile/no-print: four large numbered raised cards; read every question exactly, with two OPEN and two ASSUMES spaces.",
    "some-not-all-mat": "Four boxes: SOME, A names some strip-makers, not everyone; NAMED PERSON, Alba gives Alba's own reason; NOT ALL, Jun reports not making a strip; NOT TOLD, other people and Jun's reason remain open. Drawn strips are not an attendance count. Tactile/no-print: four raised trays with large SOME, ALBA, JUN and UNKNOWN tokens; no number of people is inferred from art.",
    "two-voice-account-mat": "Four boxes: A NOTICE, opening and some marking are stated; B ALBA, Alba says why Alba made a strip; C JUN, Jun says they read but did not make one; OPEN QUESTION, Jun has not said why and the gap must stay open. A source can be silent. Tactile/no-print: four broad raised trays and source tokens A, B, C, UNKNOWN; child directs placement.",
    "check-a-paper-gate": "Fresh Check A, fictional Paper Gate Day. Source 1: ‘Our pretend Paper Gate opened on Tuesday. On Wednesday, some visitors made paper tags to mark the opening. The notice does not name all visitors or tell why each person joined.’ Illustration: arched gate and two drawn paper tags, not attendance count. Source 2, Yara: ‘I made one paper tag because the opening mattered to me. I can now use the drawing table beyond the gate.’ Source 3, Teo: ‘I visited on Thursday. I did not make a paper tag. I have not told you why I skipped it.’ Tactile/no-print: gate arch, two rectangle tags and numbered source tokens; read exact full sources and prompts in STUDENT-CHECKS.md without hints.",
    "check-b-chalk-board": "Fresh Check B, fictional Chalk Board Opening. Source 1: ‘Our pretend room opened a new chalk board on Monday. On Friday, some visitors put paper shapes nearby to remember the opening. This notice does not say who all the visitors were or how they felt.’ Illustration: large rectangular board, one triangle and one circle, not a complete count. Source 2, Remi: ‘I put up a triangle because I helped carry the new board. I wanted to remember that day.’ Source 3, Sol: ‘I looked at the board on Saturday. I did not put up a shape. I have not said why.’ Source 4 draft cards: A ‘Why did everybody celebrate with shapes?’ B ‘Did you put up a shape? If you want, what did the board opening mean to you? You may pass.’ Tactile/no-print: rectangle board, triangle, circle, four numbered source tokens and A/B question cards; read exact full check and prompts in STUDENT-CHECKS.md without hints.",
}


def write():
    for stem, fn in PAGES.items():
        svg = HERE / f"{stem}.svg"
        pdf = HERE / f"{stem}.pdf"
        svg.write_text(fn(), encoding="utf-8")
        env = dict(os.environ, SOURCE_DATE_EPOCH="1780000000")
        with pdf.open("wb") as output:
            subprocess.run(
                ["rsvg-convert", "--format", "pdf", str(svg)],
                check=True,
                stdout=output,
                env=env,
            )
    out = [
        "# Exact linear readings and tactile/no-print routes",
        "",
        "PDF text is selectable. These readings describe every information-bearing drawing, source statement and question needed for the same task. Read the complete [source script](../MATERIALS.md) or [fresh checks](../STUDENT-CHECKS.md) neutrally. Colour is never the only cue. A constructed tactile route is not a tested accessibility accommodation. No worked check key appears here.",
        "",
    ]
    for stem, reading in ALTS.items():
        out += [f"## {stem}.svg / {stem}.pdf", "", reading, ""]
    (HERE / "TEXT-ALTERNATIVES.md").write_text("\n".join(out), encoding="utf-8")


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--regenerate", action="store_true")
    parser.parse_args()
    write()
    print("Generated eight A4 SVG/PDF pairs and exact alternatives")


if __name__ == "__main__":
    main()
