#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""Build five original low-ink A4 SVG mats and searchable PDF exports.

Default mode is a read-only SVG comparison. --write deliberately refreshes all
five SVG/PDF pairs after an author reviews the drawing code.
"""
from __future__ import annotations

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

HERE = Path(__file__).resolve().parent
RIGHTS = "SubjectNest by NeuroForgeIO Pty Ltd · CC BY 4.0 · 2026"


def t(x: float, y: float, label: str, size: float = 4.3, bold: bool = False) -> str:
    weight = 700 if bold else 400
    return f'<text x="{x}" y="{y}" font-size="{size}" font-weight="{weight}">{escape(label)}</text>'


def box(x: float, y: float, w: float, h: float) -> str:
    return f'<rect x="{x}" y="{y}" width="{w}" height="{h}" rx="1.5" fill="white" stroke="black" stroke-width="0.65"/>'


def line(x1: float, y1: float, x2: float, y2: float) -> str:
    return f'<path d="M{x1} {y1} L{x2} {y2}" fill="none" stroke="black" stroke-width="0.55"/>'


def page(title: str, desc: str, body: list[str]) -> str:
    return "\n".join([
        '<svg xmlns="http://www.w3.org/2000/svg" width="210mm" height="297mm" viewBox="0 0 210 297" role="img" aria-labelledby="title desc">',
        f'<title id="title">{escape(title)}</title>',
        f'<desc id="desc">{escape(desc)}</desc>',
        '<rect width="210" height="297" fill="white"/>',
        '<g fill="black" font-family="DejaVu Sans, sans-serif">',
        t(12, 20, title, 7.0, True), line(12, 25, 198, 25),
        *body,
        line(12, 282, 198, 282), t(12, 289, RIGHTS, 2.8),
        '</g>', '</svg>', ''
    ])


def science() -> str:
    body = [t(12, 34, 'Write REAL OBSERVATION, MODEL or PLAN before a claim.', 4.0, True)]
    for x, y, heading in [(12, 43, 'Question / prediction'), (107, 43, 'What stayed the same?'), (12, 142, 'Numbers / observations'), (107, 142, 'What can I conclude?')]:
        body += [box(x, y, 91, 91), t(x + 4, y + 10, heading, 4.1, True)]
        for dy in (29, 47, 65, 82):
            body.append(line(x + 4, y + dy, x + 86, y + dy))
    body += [t(12, 245, 'Actual source or model label: __________________________________________', 3.9),
             t(12, 258, 'Still unknown / next fair test: __________________________________________', 3.9),
             t(12, 271, 'Closed samples only. No hot water, dust or unidentified handling.', 3.3)]
    return page('Year 3 Science · claim and limit mat', 'Four blank panels headed question or prediction, what stayed the same, numbers or observations, and what can I conclude. Two lower lines ask for actual source or model label and still unknown or next fair test. No result or answer is prefilled.', body)


def hass() -> str:
    body = [t(12, 34, 'Name the source. An invented card is not a real school rule.', 4.0, True)]
    for y, heading in [(43, 'SOURCE: title / date / real or invented'), (104, 'SUPPORTED DETAIL: what it actually says')]:
        body += [box(12, y, 186, 53), t(17, y + 10, heading, 4.0, True)]
        for dy in (30, 45): body.append(line(17, y + dy, 192, y + dy))
    body += [t(12, 171, 'PERSPECTIVE A', 4.0, True), t(110, 171, 'PERSPECTIVE B', 4.0, True),
             box(12, 177, 88, 39), box(108, 177, 90, 39),
             t(12, 231, 'Action I propose: _________________________________________________', 3.9),
             t(12, 247, 'Possible help: __________________  Possible difficulty: _______________', 3.5),
             t(12, 263, 'What a real place still needs checked: ______________________________', 3.6)]
    return page('Year 3 HASS · source to proposal mat', 'Two long blank panels for source identity and supported detail, two smaller perspective boxes, and three lines for proposed action, possible help or difficulty, and what needs checking at a real place.', body)


def hpe() -> str:
    body = [t(12, 34, 'Participation is optional; use the actual school access and safety plan.', 3.8, True)]
    for x, heading in [(12, 'ASK'), (75, 'HEAR'), (138, 'CHOOSE')]:
        body += [box(x, 44, 60, 49), t(x + 5, 56, heading, 4.5, True), line(x + 5, 74, x + 54, 74)]
    body += [t(12, 111, 'NEW ROUTE: start, keep-clear zone and wide finish', 4.1, True),
             box(12, 118, 186, 100), box(83, 151, 40, 32),
             t(87, 168, 'KEEP', 4.2, True), t(87, 176, 'CLEAR', 4.2, True),
             '<circle cx="28" cy="140" r="5" fill="white" stroke="black" stroke-width="0.7"/>',
             '<circle cx="28" cy="197" r="5" fill="white" stroke="black" stroke-width="0.7"/>',
             '<circle cx="180" cy="168" r="7" fill="white" stroke="black" stroke-width="0.7"/>',
             t(38, 142, 'START A', 3.6), t(38, 199, 'START B', 3.6), t(158, 185, 'FINISH', 3.6),
             t(12, 237, 'Agreed pause / turn rule: ___________________________________________', 3.8),
             t(12, 251, 'Actual movement? [ ] yes  [ ] plan only   Partner agreed? [ ] yes [ ] no', 3.5),
             t(12, 265, 'What feedback actually happened? ____________________________________', 3.5)]
    return page('Year 3 HPE · ask, route and evidence mat', 'Three blank ask, hear and choose boxes; a wide route with two starts, a central keep-clear rectangle and one finish; lower lines for turn rule, actual movement versus plan, consent and observed feedback.', body)


def technologies() -> str:
    body = [t(12, 34, 'Design need and actual test are different from a paper algorithm.', 3.9, True)]
    body += [box(12, 43, 88, 84), box(108, 43, 90, 84),
             t(17, 54, 'USER / CRITERIA', 4.2, True), t(113, 54, 'DESIGN / TEST', 4.2, True),
             t(12, 144, 'PAPER ROBOT: one step, decision and repeat', 4.1, True)]
    for i, heading in enumerate(('1  SEQUENCE', '2  IF / ELSE', '3  REPEAT')):
        y = 153 + 31 * i
        body += [box(12, y, 186, 27), t(17, y + 9, heading, 3.8, True), line(17, y + 19, 193, y + 19)]
    body += [t(12, 261, 'Actual trial / paper trace: __________  What needs changing? ______________', 3.4)]
    return page('Year 3 Technologies · design and algorithm mat', 'Two blank top boxes for user and criteria then design and test, followed by three large blank algorithm rows labelled sequence, if or else, and repeat. A final line distinguishes actual trial from paper trace and asks what changed.', body)


def arts() -> str:
    body = [t(12, 34, 'Choose Dance, Drama, Media, Music or Visual Arts.', 4.0, True),
            box(12, 43, 88, 99), box(108, 43, 90, 99),
            t(17, 54, 'FIRST IDEA', 4.2, True), t(113, 54, 'REVISION', 4.2, True),
            t(12, 156, 'What should a chosen audience notice? _____________________________', 3.8),
            t(12, 169, 'Why did you change one element? ________________________________', 3.8),
            t(12, 185, 'PULSE / FRAME STRIP (mark the actual mode used)', 3.8, True)]
    for i in range(8):
        x = 12 + i * 23.25
        body += [box(x, 192, 22, 23), t(x + 8, 207, str(i + 1), 3.3)]
    body += [t(12, 232, '[ ] actual movement  [ ] enacted scene  [ ] approved media tool', 3.5),
             t(12, 246, '[ ] sounded music  [ ] visual work  [ ] paper plan only', 3.5),
             t(12, 261, 'Shared privately? [ ] yes [ ] no   Audience: _______________________', 3.5)]
    return page('Year 3 Arts · first idea, revision and mode', 'Two large blank first-idea and revision boxes, lines for audience attention and reason for change, eight numbered pulse or frame boxes, and checkboxes for actual movement, enacted scene, media tool, sounded music, visual work or paper plan.', body)


PAGES = {"science": science, "hass": hass, "hpe": hpe, "technologies": technologies, "arts": arts}


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--write", action="store_true")
    args = parser.parse_args()
    for name, make in PAGES.items():
        svg = HERE / f"{name}-aid.svg"
        expected = make().encode("utf-8")
        if args.write:
            svg.write_bytes(expected)
            subprocess.run(["rsvg-convert", "-f", "pdf", "-o", str(svg.with_suffix('.pdf')), str(svg)], check=True)
        elif not svg.is_file() or svg.read_bytes() != expected:
            raise SystemExit(f"FAIL: {svg.name} missing or differs; use --write after review")
    print("PASS: five original A4 SVG mats" + (" and PDF exports" if args.write else " match drawing source"))


if __name__ == "__main__":
    main()
