# SPDX-License-Identifier: Apache-2.0
"""Generate eight original A4 HASS W25–26 illustrated 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 lines(items, x, y, step=37, 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 · W25–26", 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 book(x, y, scale=1):
    w, h = 91 * scale, 65 * scale
    return (
        box(x, y, w, h, "white", 3)
        + stroke(f"M{x + w / 2} {y} L{x + w / 2} {y + h}", 3)
        + stroke(
            f"M{x + 13 * scale} {y + 21 * scale} L{x + 34 * scale} {y + 21 * scale}", 2
        )
    )


def table(x, y, scale=1):
    w = 111 * scale
    return box(x, y, w, 32 * scale, SAND, 14) + stroke(
        f"M{x + 20 * scale} {y + 32 * scale} L{x + 20 * scale} {y + 88 * scale} M{x + w - 20 * scale} {y + 32 * scale} L{x + w - 20 * scale} {y + 88 * scale}",
        4,
    )


def shelf(x, y):
    return box(x, y, 105, 105, "white", 3) + stroke(
        f"M{x + 10} {y + 47} L{x + 95} {y + 47} M{x + 16} {y + 27} L{x + 16} {y + 43} M{x + 48} {y + 18} L{x + 48} {y + 43} M{x + 74} {y + 25} L{x + 74} {y + 43}",
        3,
    )


def mat(x, y):
    return circle(x, y, 47, GREEN) + stroke(f"M{x - 21} {y} L{x + 21} {y}", 3)


def chair(x, y):
    return circle(x, y + 40, 28, LILAC) + box(x - 24, y - 8, 48, 40, LILAC, 10)


def board_main():
    b = word(48, 194, "SOURCE A · FIVE ANONYMOUS CHOICES", 18, True)
    b += box(60, 221, 674, 580, BLUE)
    b += word(87, 274, "QUIET BOOK NOOK · THREE", 20, True)
    for x in (129, 304, 479):
        b += book(x, 326)
    b += word(87, 479, "BROAD MAKING TABLE · TWO", 20, True)
    for x in (206, 480):
        b += table(x, 533)
    b += lines(
        [
            "“Five made-up visitors each chose one feature",
            "for a pretend afternoon: three chose the quiet",
            "book nook; two chose the broad making table.",
            "The board does not name the five visitors or",
            "say why they chose.”",
        ],
        66,
        838,
        31,
        17,
    )
    return page(
        "Paper Hall · choice board",
        "Three book tokens; two table tokens; nobody named.",
        b,
    )


def ada():
    b = word(48, 194, "SOURCE B · ADA'S PERSONAL NOTE", 19, True)
    b += box(62, 222, 670, 708, GREEN) + book(306, 301, 2)
    b += lines(
        [
            "“I choose the quiet book nook because I like",
            "looking at picture books there.”",
        ],
        78,
        566,
        49,
        19,
    )
    b += box(86, 712, 620, 112, "white")
    b += word(107, 762, "ADA'S REASON · NOT AN A TOKEN ID", 19, True)
    b += word(82, 887, "A's anonymous board does not name Ada.", 16)
    return page(
        "Ada · book nook reason",
        "A named person's note stays separate from anonymous tokens.",
        b,
    )


def bo():
    b = word(48, 194, "SOURCE C · BO'S PERSONAL NOTE", 19, True)
    b += box(62, 222, 670, 708, SAND) + table(307, 331, 1.65)
    b += polygon("374,295 431,295 451,332 354,332", BLUE)
    b += lines(
        [
            "“I choose the broad making table because I can",
            "spread out my paper model there.”",
        ],
        78,
        566,
        49,
        19,
    )
    b += box(86, 712, 620, 112, "white")
    b += word(107, 762, "BO'S REASON · NOT AN A TOKEN ID", 19, True)
    b += word(82, 887, "A's anonymous board does not name Bo.", 16)
    return page(
        "Bo · making table reason",
        "A second individual reason does not explain all votes.",
        b,
    )


def draft():
    b = word(48, 194, "SOURCE D · NERI'S UNIMPLEMENTED PROPOSAL", 17, True)
    items = [
        ("COUNT FROM A", "Three chose nook; two chose table.", BLUE),
        ("EVERYONE CLAIM", "Everyone wants quiet.", LILAC),
        ("PROPOSED CHANGE", "We should close the making table tomorrow.", SAND),
    ]
    for i, (heading, detail, fill) in enumerate(items):
        y = 225 + i * 232
        b += box(65, y, 665, 196, fill) + circle(120, y + 93, 34, "white")
        b += word(108, y + 101, "?", 29, True)
        b += word(180, y + 71, heading, 19, True)
        b += word(180, y + 125, detail, 17)
    b += word(65, 967, "A proposal is not a decision, action or result.", 16)
    return page(
        "Neri · paper proposal",
        "Only the small count is supported; closure has not happened.",
        b,
    )


def count_mat():
    b = word(48, 194, "KEEP COUNT, VOICE AND UNKNOWN APART", 18, True)
    items = [
        ("A · FIVE CHOICES", "Three nook, two table; people unnamed.", BLUE),
        ("B · ADA'S REASON", "Picture books matter to Ada.", GREEN),
        ("C · BO'S REASON", "Model space matters to Bo.", SAND),
        ("NOT TOLD", "Whose anonymous token? Why all chose?", LILAC),
    ]
    for i, (head, prompt, fill) in enumerate(items):
        y = 225 + i * 184
        b += box(61, y, 672, 157, fill) + circle(111, y + 77, 28, "white")
        b += word(102, y + 84, str(i + 1), 22, True)
        b += word(166, y + 59, head, 18, True)
        b += word(166, y + 107, prompt, 16)
    b += word(63, 1006, "A name card never proves ownership of an anonymous token.", 15)
    return page(
        "Count, voice, unknown mat",
        "A small tally and individual reasons have different scopes.",
        b,
    )


def question_mat():
    b = word(48, 194, "BEFORE ANY SHARED-PLACE CHANGE, ASK", 18, True)
    items = [
        ("WHO USES IT?", "A's five tokens leave names and other users open.", BLUE),
        ("WHAT MIGHT CHANGE?", "Bo's use might need a place; outcome unknown.", GREEN),
        ("WHAT SOURCE NEXT?", "Voluntary account or approved observation?", SAND),
        ("WHO MAY DECIDE?", "A paper helper's proposal is not permission.", LILAC),
    ]
    for i, (head, prompt, fill) in enumerate(items):
        y = 225 + i * 184
        b += box(61, y, 672, 157, fill)
        b += word(84, y + 48, head, 19, True) + word(84, y + 91, prompt, 16)
        b += stroke(f"M84 {y + 129} L706 {y + 129}", 2)
    b += word(
        63, 1006, "A planned question or source is not a result already obtained.", 15
    )
    return page(
        "Before-change question mat",
        "Four questions prevent a token count from becoming a rule.",
        b,
    )


def check_a():
    b = word(48, 194, "FRESH CHECK A · WILLOW ROOM", 18, True)
    b += box(59, 219, 678, 317, BLUE)
    b += word(77, 253, "SOURCE 1 · FIVE ANONYMOUS CHOICES", 16, True)
    for x in (81, 205, 329, 453):
        b += shelf(x, 281)
    b += mat(647, 333)
    b += word(79, 416, "FOUR DRAWING SHELF · ONE QUIET MAT", 16, True)
    b += lines(
        [
            "Five pretend visitors each chose one Willow Room feature:",
            "four chose the drawing shelf; one chose the quiet mat.",
            "The board does not name the five visitors or give",
            "their reasons.",
        ],
        79,
        445,
        20,
        13,
    )
    b += box(59, 551, 678, 181, GREEN)
    b += lines(
        [
            "SOURCE 2 · IRI",
            "“I choose the quiet mat because I like looking",
            "at leaf cards there.”",
        ],
        77,
        587,
        42,
        16,
    )
    b += box(59, 750, 678, 246, SAND)
    b += lines(
        [
            "SOURCE 3 · REN",
            "“I choose the drawing shelf because I can lay out",
            "my paper leaves there.”",
            "A does not identify Iri's or Ren's token.",
        ],
        77,
        791,
        43,
        16,
    )
    return page(
        "Check A · Willow Room", "Fresh five-choice board and two named reasons.", b
    )


def check_b():
    b = word(48, 194, "FRESH CHECK B · FOLDED ROOM", 18, True)
    b += box(59, 218, 678, 252, BLUE)
    b += word(77, 250, "SOURCE 1 · SIX ANONYMOUS CHOICES", 16, True)
    for x in (77, 188, 299, 410):
        b += table(x, 279, 0.68)
    b += chair(577, 294) + chair(682, 294)
    b += word(77, 386, "FOUR CRAFT TABLE · TWO READING CHAIR", 15, True)
    b += lines(
        [
            "Six pretend visitors each chose one feature:",
            "four chose the smooth craft table; two chose the",
            "soft reading chair. The board does not name",
            "anyone or say why they chose.",
        ],
        77,
        403,
        17,
        13,
    )
    b += box(59, 485, 678, 132, GREEN)
    b += lines(
        [
            "SOURCE 2 · ORI",
            "“I choose the reading chair because I like to",
            "look at books there.”",
        ],
        77,
        514,
        35,
        15,
    )
    b += box(59, 632, 678, 132, SAND)
    b += lines(
        [
            "SOURCE 3 · VAL",
            "“I choose the craft table because I can",
            "spread my folded paper there.”",
        ],
        77,
        661,
        35,
        15,
    )
    b += box(59, 779, 678, 220, LILAC)
    b += lines(
        [
            "SOURCE 4 · KEEPER'S PAPER PROPOSAL",
            "“Four chose the table and two chose the chair.",
            "Everyone wants making, so we should close the",
            "chair tomorrow.” PROPOSED · NOT DONE",
        ],
        77,
        816,
        42,
        16,
    )
    return page(
        "Check B · Folded Room", "Fresh six-choice board, two voices and a proposal.", b
    )


PAGES = {
    "paper-hall-choice-board": board_main,
    "ada-book-nook": ada,
    "bo-making-table": bo,
    "neri-proposal": draft,
    "count-voice-unknown-mat": count_mat,
    "before-change-question-mat": question_mat,
    "check-a-willow-room": check_a,
    "check-b-folded-room": check_b,
}

ALTS = {
    "paper-hall-choice-board": "Source A, fictional Paper Hall choice board. Five anonymous choice tokens: three separate open-book icons under QUIET BOOK NOOK; two separate broad-table icons under BROAD MAKING TABLE. Exact words: ‘Five made-up visitors each chose one feature for a pretend afternoon: three chose the quiet book nook; two chose the broad making table. The board does not name the five visitors or say why they chose.’ Tactile/no-print: give exactly three raised book tiles and two table tiles, one tile per made-up choice; leave name/reason boxes blank. No real vote occurred.",
    "ada-book-nook": "Source B, Ada's individual note beside a large book illustration. Exact words: ‘I choose the quiet book nook because I like looking at picture books there.’ The page marks ADA'S REASON · NOT AN A TOKEN ID. A's anonymous board does not name Ada. Tactile/no-print: one book shape and Ada name card stay physically apart from A's five anonymous tokens.",
    "bo-making-table": "Source C, Bo's individual note beside a broad table and paper-model illustration. Exact words: ‘I choose the broad making table because I can spread out my paper model there.’ The page marks BO'S REASON · NOT AN A TOKEN ID. A's anonymous board does not name Bo. Tactile/no-print: one table and folded-paper outline with Bo name card stay physically apart from A's tokens.",
    "neri-proposal": "Source D, helper Neri's unimplemented paper proposal. Three boxes: COUNT FROM A, ‘Three chose nook; two chose table’; EVERYONE CLAIM, ‘Everyone wants quiet’; PROPOSED CHANGE, ‘We should close the making table tomorrow.’ Only the count follows from A. A proposal is not a decision, action or result. Tactile/no-print: three raised claim strips labelled COUNT, EVERYONE and PROPOSED; no DONE token is supplied.",
    "count-voice-unknown-mat": "Four boxes: A FIVE CHOICES, three nook/two table with people unnamed; B ADA'S REASON, picture books matter to Ada; C BO'S REASON, model space matters to Bo; NOT TOLD, whose anonymous token and why all chose. A name card never proves ownership of an anonymous token. Tactile/no-print: four raised trays; keep named cards apart from anonymous tokens.",
    "before-change-question-mat": "Four question boxes: WHO USES IT? A's five tokens leave names/other users open; WHAT MIGHT CHANGE? Bo's use might need a place and outcome is unknown; WHAT SOURCE NEXT? voluntary account or approved observation; WHO MAY DECIDE? a paper helper's proposal is not permission. A planned source is not a result already obtained. Tactile/no-print: four numbered raised strips with blank answer places.",
    "check-a-willow-room": "Fresh Check A, fictional Willow Room. Source 1: ‘Five pretend visitors each chose one Willow Room feature: four chose the drawing shelf; one chose the quiet mat. The board does not name the five visitors or give their reasons.’ Four drawing-shelf squares and one quiet-mat circle. Source 2, Iri: ‘I choose the quiet mat because I like looking at leaf cards there.’ Source 3, Ren: ‘I choose the drawing shelf because I can lay out my paper leaves there.’ Anonymous Source 1 cannot identify Iri or Ren. Tactile/no-print: exactly four raised shelf tiles and one round mat tile, plus separately named Iri/Ren source cards; read full prompts in STUDENT-CHECKS.md without hints.",
    "check-b-folded-room": "Fresh Check B, fictional Folded Room. Source 1: ‘Six pretend visitors each chose one feature: four chose the smooth craft table; two chose the soft reading chair. The board does not name anyone or say why they chose.’ Four small rectangular craft-table icons and two round reading-chair icons. Source 2, Ori: ‘I choose the reading chair because I like to look at books there.’ Source 3, Val: ‘I choose the craft table because I can spread my folded paper there.’ Source 4 proposal: ‘Four chose the table and two chose the chair. Everyone wants making, so we should close the chair tomorrow.’ PROPOSED · NOT DONE. Tactile/no-print: four table and two chair tiles plus separate Ori/Val name cards; read full 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 alternatives describe every information-bearing illustration, source statement and unknown 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()
