# SPDX-License-Identifier: Apache-2.0
"""Build eight original A4 HASS W21–22 illustrated pages and exact 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"
PALE_BLUE = "#e2f1f8"
PALE_GREEN = "#e2f3e8"
PALE_ORANGE = "#fff0db"
PALE_PURPLE = "#f1e9f8"


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


def text(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 path(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=38, size=17):
    return "".join(text(x, y + i * step, item, size) for i, 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"/>',
            rect(29, 30, 736, 1063),
            text(46, 74, "SUBJECTNEST · FOUNDATION HASS · W21–22", 17, True),
            text(46, 115, title, 25, True),
            text(46, 148, desc, 14),
            body,
            path("M46 1041 L748 1041", 2),
            text(
                46,
                1070,
                "Original fictional source · CC BY 4.0 · exact alternatives in pack",
                11,
            ),
            "</svg>",
            "",
        ]
    )


def table_icon(x, y, w=180):
    return rect(x, y, w, 39, PALE_ORANGE, 18) + path(
        f"M{x + 27} {y + 39} L{x + 27} {y + 112} M{x + w - 27} {y + 39} L{x + w - 27} {y + 112}",
        6,
    )


def water_icon(x, y):
    return circle(x + 48, y + 57, 48, PALE_BLUE) + path(
        f"M{x + 48} {y + 20} C{x + 12} {y + 64} {x + 30} {y + 86} {x + 48} {y + 87} C{x + 72} {y + 88} {x + 84} {y + 65} {x + 48} {y + 20}",
        4,
    )


def seat_icon(x, y):
    return (
        rect(x, y + 48, 149, 56, PALE_PURPLE, 30)
        + rect(x + 13, y, 123, 58, PALE_PURPLE, 20)
        + path(f"M{x + 24} {y + 59} Q{x + 74} {y + 78} {x + 125} {y + 59}", 3)
    )


def directory():
    b = text(48, 194, "SOURCE A · NIA'S FEATURE DIRECTORY", 18, True)
    tiles = [
        (227, PALE_ORANGE, table_icon(92, 266), "BROAD STORY TABLE"),
        (450, PALE_BLUE, water_icon(123, 492), "ROUND WATER POINT"),
        (673, PALE_PURPLE, seat_icon(96, 713), "SOFT SEAT"),
    ]
    for y, fill, icon, label in tiles:
        b += rect(57, y, 680, 202, fill) + icon + text(325, y + 104, label, 19, True)
    b += lines(
        [
            "“Our pretend room has a broad story table, a round water",
            "point and a soft seat. This list names features; it does not",
            "say how everybody feels or whether the room is accessible.”",
        ],
        62,
        920,
        30,
        14,
    )
    b += text(62, 1017, "Not a position map or real access record.", 13)
    return page(
        "Paper Commons · features", "Three feature icons, no route or real place.", b
    )


def emi():
    b = text(48, 194, "SOURCE B · EMI'S INDIVIDUAL NOTE", 18, True)
    b += rect(61, 223, 674, 707, PALE_PURPLE) + seat_icon(314, 283)
    b += lines(
        [
            "“The soft seat matters to me because",
            "I like to look quietly at picture books there.”",
        ],
        78,
        502,
        46,
        19,
    )
    b += rect(84, 651, 626, 144, "white")
    b += text(108, 700, "SHARE STATUS · NOT ASKED", 20, True)
    b += text(108, 746, "Emi has not been asked about a public guide.", 16)
    b += text(83, 873, "One person's reason is not everybody's reason.", 16)
    return page(
        "Emi · view held private",
        "Knowing a view is different from permission to share.",
        b,
    )


def fin():
    b = text(48, 194, "SOURCE C · FIN'S INDIVIDUAL NOTE", 18, True)
    b += rect(61, 223, 674, 707, PALE_GREEN) + table_icon(307, 289, 180)
    b += rect(358, 265, 78, 48, "white", 2) + path("M370 287 Q391 269 421 286", 3)
    b += lines(
        [
            "“The story table matters to me because",
            "I can show my paper drawings there.”",
        ],
        78,
        503,
        46,
        19,
    )
    b += rect(83, 649, 628, 157, "white")
    b += text(106, 694, "FICTIONAL SHARE STATUS · EXACT SENTENCE OK", 17, True)
    b += text(106, 739, "Fin agrees to this sentence with Fin named", 16)
    b += text(106, 775, "in the pretend paper guide only.", 16)
    b += text(82, 876, "This is not consent from a real person.", 16)
    return page(
        "Fin · one permitted view",
        "A bounded fictional permission, not real consent.",
        b,
    )


def draft():
    b = text(48, 194, "SOURCE D · REN'S UNPOSTED GUIDE DRAFT", 18, True)
    cards = [
        (
            225,
            PALE_BLUE,
            "FEATURES · supported by A",
            "Paper Commons has a story table, water point and soft seat.",
        ),
        (
            455,
            PALE_ORANGE,
            "EMI QUOTE · wait",
            "Emi says the soft seat matters to Emi.",
        ),
        (
            686,
            PALE_PURPLE,
            "EVERYONE CLAIM · no evidence",
            "Everyone feels calm there.",
        ),
    ]
    for y, fill, heading, detail in cards:
        b += rect(65, y, 665, 200, fill)
        b += circle(119, y + 94, 37, "white")
        b += text(101, y + 103, "?", 33, True)
        b += text(178, y + 79, heading, 18, True)
        b += text(178, y + 126, detail, 16)
    b += text(66, 977, "A draft is not a posted guide or permission.", 16)
    return page(
        "Paper Commons · draft guide",
        "Three clauses require three different decisions.",
        b,
    )


def audience_mat():
    b = text(48, 194, "ASK TWO QUESTIONS ABOUT EACH SOURCE", 19, True)
    labels = [
        ("SOURCE SUPPORTS IT?", "Where is the exact feature or view?", PALE_BLUE),
        ("WHOSE VIEW?", "One named person is not everyone.", PALE_GREEN),
        ("MAY IT BE SHARED?", "Check exact status and intended audience.", PALE_ORANGE),
        ("STILL UNKNOWN", "Do not fill a gap with a guess.", PALE_PURPLE),
    ]
    for i, (heading, prompt, fill) in enumerate(labels):
        y = 226 + i * 185
        b += rect(60, y, 674, 155, fill)
        b += circle(115, y + 77, 29, "white")
        b += text(105, y + 86, str(i + 1), 23, True)
        b += text(167, y + 61, heading, 19, True)
        b += text(167, y + 107, prompt, 16)
    b += text(63, 1008, "Real publication needs real approval under school policy.", 15)
    return page(
        "Audience and source sort mat",
        "Four places for evidence, view, status and unknown.",
        b,
    )


def guide_mat():
    b = text(48, 194, "BUILD A CAREFUL PRETEND VISITOR GUIDE", 18, True)
    labels = [
        ("FEATURE FROM A", "A names a feature; no route is shown.", PALE_BLUE),
        ("PERMITTED EXACT VIEW", "Name the person and check exact status.", PALE_GREEN),
        (
            "LEAVE OFF THE GUIDE",
            "Unasked/declined quote or unsupported all claim.",
            PALE_ORANGE,
        ),
        ("QUESTION STILL OPEN", "What approved source would help next?", PALE_PURPLE),
    ]
    for i, (heading, prompt, fill) in enumerate(labels):
        y = 224 + i * 185
        b += rect(60, y, 674, 157, fill)
        b += text(84, y + 46, heading, 18, True)
        b += text(84, y + 88, prompt, 15)
        b += path(f"M85 {y + 128} L706 {y + 128}", 2)
    b += text(62, 1004, "Paper rehearsal only; choose private sharing.", 15)
    return page(
        "Careful guide builder",
        "Sourced feature, bounded view, held item and question.",
        b,
    )


def check_a():
    b = text(48, 194, "FRESH CHECK A · RIBBON ROOM", 18, True)
    b += rect(59, 219, 678, 312, PALE_BLUE)
    b += text(77, 253, "SOURCE 1 · PAZ'S FEATURE CARD", 17, True)
    b += table_icon(120, 309, 170) + rect(485, 290, 128, 128, "white", 2)
    b += lines(
        [
            "“The pretend Ribbon Room has an oval table",
            "and a square notice board.”",
        ],
        75,
        466,
        29,
        15,
    )
    b += rect(59, 545, 678, 188, PALE_ORANGE)
    b += lines(
        [
            "SOURCE 2 · NOLA · NOT ASKED TO SHARE",
            "“The oval table matters to me because",
            "I can sort paper boats there.”",
        ],
        78,
        581,
        41,
        16,
    )
    b += rect(59, 751, 678, 246, PALE_PURPLE)
    b += lines(
        [
            "SOURCE 3 · LU'S UNPOSTED GUIDE",
            "“Ribbon Room has an oval table and square",
            "notice board. Nola says the table matters to",
            "Nola. Everyone loves the oval table.”",
        ],
        78,
        791,
        43,
        16,
    )
    return page(
        "Check A · Ribbon Room",
        "New features, a not-asked view and an unposted draft.",
        b,
    )


def check_b():
    b = text(48, 194, "FRESH CHECK B · COPPER ROOM", 18, True)
    b += rect(59, 217, 678, 262, PALE_BLUE)
    b += text(75, 249, "SOURCE 1 · KEEPER'S FEATURE CARD", 16, True)
    b += polygon("157,289 90,393 224,393", PALE_ORANGE) + circle(
        539, 343, 63, PALE_GREEN
    )
    b += lines(
        [
            "“The pretend Copper Room has a triangular craft desk",
            "and a round reading cushion.”",
        ],
        80,
        428,
        29,
        14,
    )
    b += rect(59, 492, 678, 164, PALE_GREEN)
    b += lines(
        [
            "SOURCE 2 · MIKO · EXACT SENTENCE OK",
            "“I like the triangular craft desk for folding paper.”",
            "Pretend guide may use these exact words with Miko named.",
        ],
        77,
        525,
        41,
        15,
    )
    b += rect(59, 671, 678, 164, PALE_ORANGE)
    b += lines(
        [
            "SOURCE 3 · DARA · PUBLIC USE DECLINED",
            "“I like the round reading cushion for quiet looking.”",
            "Do not put Dara's sentence in the pretend public guide.",
        ],
        77,
        704,
        41,
        15,
    )
    b += rect(59, 850, 678, 148, PALE_PURPLE)
    b += lines(
        [
            "SOURCE 4 · EDITOR'S UNPOSTED DRAFT",
            "“The room has a triangular craft desk and a",
            "round reading cushion. Miko and Dara like",
            "these features. All visitors enjoy the room.”",
        ],
        77,
        879,
        31,
        15,
    )
    return page(
        "Check B · Copper Room",
        "New features, one permitted view and one declined view.",
        b,
    )


PAGES = {
    "paper-commons-features": directory,
    "emi-view-hold": emi,
    "fin-view-ok": fin,
    "public-guide-draft": draft,
    "audience-source-sort-mat": audience_mat,
    "guide-builder-mat": guide_mat,
    "check-a-ribbon-room": check_a,
    "check-b-copper-room": check_b,
}

ALTS = {
    "paper-commons-features": "Source A is Nia's invented Paper Commons feature directory. Three separate icons show a broad story table (oblong top and two legs), a round water point (circle and droplet) and a soft seat (padded back and cushion). Exact source statement: ‘Our pretend room has a broad story table, a round water point and a soft seat. This list names features; it does not say how everybody feels or whether the room is accessible.’ It is not a map. Tactile/no-print: offer an oblong table top with two short sticks, one ring/drop token and a padded rectangle; read exact words in MATERIALS.md.",
    "emi-view-hold": "Source B, Emi's invented personal note beside a soft-seat icon. Exact words: ‘The soft seat matters to me because I like to look quietly at picture books there.’ Status: Emi has not been asked to share this note in a public guide. Tactile/no-print: offer one padded rectangle, an Emi token and a separate NOT ASKED sleeve; read exact sentence and status. This is one person's reason.",
    "fin-view-ok": "Source C, Fin's invented personal note beside a story-table icon and paper drawing. Exact words: ‘The story table matters to me because I can show my paper drawings there.’ Fictional status: Fin agrees that this exact sentence may appear in the pretend paper guide with Fin named. This is not consent from a real person. Tactile/no-print: use a table top, drawing card, Fin token and EXACT SENTENCE OK tile; read exact words.",
    "public-guide-draft": "Source D is Ren's unposted fictional guide draft. The first box says ‘Paper Commons has a story table, water point and soft seat’; this is supported by A. The second says ‘Emi says the soft seat matters to Emi’; Emi has not been asked about public sharing. The third says ‘Everyone feels calm there’; there is no all-visitor evidence. A draft is not a posted guide or permission. Tactile/no-print: use three separate strips labelled FEATURE, QUOTE WAIT and EVERYONE UNKNOWN; read exact draft in MATERIALS.md.",
    "audience-source-sort-mat": "Four boxes in order: 1 SOURCE SUPPORTS IT? Where is the exact feature or view? 2 WHOSE VIEW? One named person is not everyone. 3 MAY IT BE SHARED? Check exact status and intended audience. 4 STILL UNKNOWN: do not fill a gap with a guess. Real publication needs real approval under school policy. Tactile/no-print: use four numbered raised trays and read each heading/prompt.",
    "guide-builder-mat": "Four lined boxes: FEATURE FROM A, A names a feature and no route is shown; PERMITTED EXACT VIEW, name the person and check exact status; LEAVE OFF THE GUIDE, unasked/declined quote or unsupported all claim; QUESTION STILL OPEN, what approved source would help next? Paper rehearsal only; private sharing is allowed. Tactile/no-print: use four raised boxes, blank strips and source-letter/status tokens.",
    "check-a-ribbon-room": "Fresh Check A, invented Ribbon Room. Source 1, keeper Paz's feature card: ‘The pretend Ribbon Room has an oval table and a square notice board.’ Two icons show oval table with legs and square board, not positions. Source 2, Nola's personal note: ‘The oval table matters to me because I can sort paper boats there.’ Status: Nola has not been asked to put this sentence in a public guide. Source 3, Lu's proposed unposted guide: ‘Ribbon Room has an oval table and square notice board. Nola says the table matters to Nola. Everyone loves the oval table.’ No all-visitor evidence or new permission. Tactile/no-print: use an oval top, two legs, square board and three numbered source tiles; read exact full check and prompts in STUDENT-CHECKS.md without hints.",
    "check-b-copper-room": "Fresh Check B, invented Copper Room. Source 1: ‘The pretend Copper Room has a triangular craft desk and a round reading cushion.’ Icons show triangle and circle, not positions. Source 2, Miko: ‘I like the triangular craft desk for folding paper.’ Fictional status permits that exact sentence with Miko named in the pretend guide. Source 3, Dara: ‘I like the round reading cushion for quiet looking.’ Fictional status declines public use. Source 4 unposted editor draft: ‘The room has a triangular craft desk and a round reading cushion. Miko and Dara like these features. All visitors enjoy the room.’ No all-visitor evidence. Tactile/no-print: use large triangle and circle, four numbered source tiles and distinct EXACT OK/DECLINED status tokens; 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",
        "",
        "The PDF text is selectable. These alternatives describe every information-bearing illustrated mark, source statement and status needed for the same task. Read the complete [source script](../MATERIALS.md) or [fresh check prompts](../STUDENT-CHECKS.md) neutrally. Colour is never the sole clue. A tactile setup is a constructed equivalent, not a tested accessibility accommodation. No child-facing worked 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()
