#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""Build four original A4 SVG aids, then searchable PDFs with rsvg-convert.

Run only with --write. Default mode compares existing SVG bytes without changes.
"""
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 txt(x: float, y: float, value: str, size: float = 4.2, weight: int = 400) -> str:
    return f'<text x="{x}" y="{y}" font-size="{size}" font-weight="{weight}">{escape(value)}</text>'


def rect(x: float, y: float, width: float, height: float, radius: float = 2) -> str:
    return f'<rect x="{x}" y="{y}" width="{width}" height="{height}" rx="{radius}" fill="white" stroke="black" stroke-width="0.6"/>'


def line(x1: float, y1: float, x2: float, y2: float) -> str:
    return f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" stroke="black" stroke-width="0.45"/>'


def page(title: str, description: 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(description)}</desc>',
        '<rect width="210" height="297" fill="white"/>',
        '<g fill="black" font-family="DejaVu Sans, sans-serif">',
        txt(12, 20, title, 7.2, 700),
        line(12, 25, 198, 25),
        *body,
        line(12, 282, 198, 282),
        txt(12, 289, RIGHTS, 2.8),
        '</g>',
        '</svg>',
        ''
    ])


def science() -> str:
    body = [txt(12, 34, 'Card / question: __________________________   Source or real test: ______________________', 3.5)]
    for x, y, heading in [(12, 43, '1. I predict'), (107, 43, '2. Action: what changed?'), (12, 145, '3. I observed'), (107, 145, '4. Same material / still unknown')]:
        body += [rect(x, y, 91, 96), txt(x + 5, y + 9, heading, 4, 700)]
        for dy in (27, 43, 59, 75):
            body.append(line(x + 5, y + dy, x + 86, y + dy))
    body += [txt(12, 251, 'Evidence I used:  [ ] saw   [ ] heard   [ ] felt   [ ] picture/model only', 3.7),
             txt(12, 262, 'Did my prediction match? ______   A fair next question: __________________________', 3.5),
             txt(12, 273, 'No sound, handling or outdoor observation is required. Label a model as a model.', 3.2)]
    return page('Year 2 Science · prediction and evidence mat', 'Four blank writing panels: prediction, action, observation, and unchanged material or unknown. A line records the evidence route and next fair question. No answer is supplied.', body)


def hass() -> str:
    body = [txt(12, 34, 'Name the source before making a claim. Invented cards are practice, not local history.', 3.5)]
    for y, heading in [(43, 'SOURCE A: title / date / real or invented'), (100, 'SOURCE B: title / date / real or invented')]:
        body += [rect(12, y, 186, 52), txt(17, y + 9, heading, 4, 700),
                 txt(17, y + 20, 'Detail the source actually supports:'), line(17, y + 32, 193, y + 32), line(17, y + 43, 193, y + 43)]
    body += [txt(12, 164, 'A careful comparison: ________________________  What is still unknown? ___________________', 3.4),
             txt(12, 177, 'Nested place model · use checked real labels or write “fictional rehearsal”', 3.8, 700),
             rect(12, 183, 186, 89),
             rect(22, 194, 166, 67),
             rect(32, 205, 146, 45),
             rect(42, 216, 126, 23),
             txt(16, 190, 'state / territory: __________', 3),
             txt(26, 201, 'region: __________', 3),
             txt(36, 212, 'district: __________', 3),
             txt(47, 229, 'small place: __________________', 3)]
    return page('Year 2 HASS · source to scale mat', 'Two blank source boxes with title, date and supported detail, a comparison and unknown line, then four nested blank place boxes from small place through district and region to state or territory. No real location is asserted.', body)


def hpe() -> str:
    body = [txt(12, 34, 'Use the paper route or an approved accessible movement route. Ask before joining.', 3.5)]
    for x, heading in [(12, 'ASK'), (76, 'LISTEN'), (140, 'CHOOSE')]:
        body += [rect(x, 44, 58, 51), txt(x + 5, 54, heading, 4.3, 700), line(x + 5, 70, x + 53, 70), line(x + 5, 84, x + 53, 84)]
    body += [txt(12, 109, 'My fair rule / pause signal: ______________________________________________________', 3.7),
             txt(12, 124, 'New route · two starts, one destination, centre block', 4.2, 700),
             rect(12, 131, 186, 115, 0), rect(87, 171, 36, 32, 0),
             txt(91, 188, 'BLOCK', 4, 700),
             '<circle cx="30" cy="157" r="5" fill="white" stroke="black" stroke-width="0.7"/>',
             '<circle cx="30" cy="223" r="5" fill="white" stroke="black" stroke-width="0.7"/>',
             '<circle cx="181" cy="190" r="5" fill="white" stroke="black" stroke-width="0.7"/>',
             txt(40, 158, 'START A', 3.5), txt(40, 224, 'START B', 3.5), txt(159, 203, 'FINISH', 3.3),
             txt(12, 258, 'Safety reason: ______________________   Tried with agreement? [ ] yes [ ] plan only', 3.4),
             txt(12, 270, 'No race, contact, hard throw or personal-disclosure task.', 3.3)]
    return page('Year 2 HPE · ask, choose and move mat', 'Three blank ask, listen and choose boxes; a blank fair-rule line; a route drawing with two starts, a centre block and one finish; and safety and tried-or-planned lines.', body)


def arts() -> str:
    body = [txt(12, 34, 'Choose one form:  [ ] Dance  [ ] Drama  [ ] Media  [ ] Music  [ ] Visual', 3.9),
            rect(12, 43, 91, 99), rect(107, 43, 91, 99),
            txt(17, 53, 'FIRST CHOICE', 4, 700), txt(112, 53, 'CHANGE / NEW CHOICE', 4, 700),
            txt(12, 155, 'What did you want a chosen viewer/listener to notice? _____________________________', 3.4),
            txt(12, 169, 'One reason for the change: ___________________________________________________', 3.4),
            txt(12, 184, 'Optional pulse strip for Music: tap or rest in each box', 3.7, 700)]
    for i in range(8):
        body += [rect(12 + i * 23.25, 191, 22, 24, 0), txt(20 + i * 23.25, 205, str(i + 1), 3)]
    body += [txt(12, 230, 'Actual mode: [ ] movement [ ] dramatic scene [ ] approved media tool', 3.5),
             txt(12, 241, '                   [ ] sounded music [ ] visual work [ ] paper plan only', 3.5),
             txt(12, 255, 'Audience chosen: __________________   Shared? [ ] yes [ ] planning only', 3.5),
             txt(12, 269, 'Media frames contain original paper work only; no child image, name or voice.', 3.2)]
    return page('Year 2 Arts · choice, change and share mat', 'Two large blank drawing panels for first and revised choices, a viewer and reason line, eight numbered pulse boxes and checkboxes for the actual art-making and sharing route.', body)


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


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--write", action="store_true", help="Write SVG and PDF files; without this flag, compare SVG only")
    args = parser.parse_args()
    for name, build in PAGES.items():
        svg = HERE / f"{name}-aid.svg"
        expected = build().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: four original A4 SVG aids" + (" and PDF exports" if args.write else " match generator"))


if __name__ == "__main__":
    main()
