#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""Render six original Foundation Science W29–30 pictorial A4 vector aids."""

from __future__ import annotations

import html
import os
import subprocess
from pathlib import Path

ROOT = Path(__file__).resolve().parent
INK = "#143746"
TEAL = "#006b70"
OCHRE = "#9e5c14"
BLUE = "#385f94"
GREEN = "#377452"
LINE = "#566c73"
PALE = "#e9f3f0"
MIST = "#f4f7f8"
SAND = "#fff5e7"
FONT = "DejaVu Sans, sans-serif"


def esc(value: str) -> str:
    return html.escape(value, quote=True)


def label(x: float, y: float, value: str, size: float = 4, bold: bool = False) -> str:
    weight = ' font-weight="700"' if bold else ""
    return (
        f'<text x="{x}" y="{y}" font-family="{FONT}" font-size="{size}"'
        f'{weight} fill="{INK}">{esc(value)}</text>'
    )


def rect(x: float, y: float, w: float, h: float, fill: str = "#fff") -> str:
    return (
        f'<rect x="{x}" y="{y}" width="{w}" height="{h}" rx="3"'
        f' fill="{fill}" stroke="{LINE}" stroke-width=".8"/>'
    )


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


def plant(cx: float, cy: float, pointed: bool) -> str:
    pieces = [line(cx, cy + 28, cx, cy - 16, GREEN)]
    places = ((cx - 19, cy - 9), (cx + 18, cy - 6), (cx - 17, cy + 13))
    for x, y in places:
        if pointed:
            pieces.append(
                f'<path d="M{cx} {y + 4} Q{x - 1} {y - 9} {x} {y - 17}'
                f' Q{x + 15} {y - 8} {cx} {y + 4} Z"'
                f' fill="{PALE}" stroke="{GREEN}" stroke-width="1.6"/>'
            )
        else:
            pieces.append(
                f'<ellipse cx="{x}" cy="{y - 4}" rx="13" ry="8"'
                f' fill="{PALE}" stroke="{GREEN}" stroke-width="1.6"/>'
            )
    pieces.append(line(cx - 23, cy + 29, cx + 23, cy + 29, LINE))
    return "\n".join(pieces)


def animal(cx: float, cy: float, pointed: bool) -> str:
    pieces = [
        (
            f'<ellipse cx="{cx}" cy="{cy + 14}" rx="26" ry="19"'
            f' fill="{SAND}" stroke="{OCHRE}" stroke-width="1.6"/>'
        )
    ]
    for x in (cx - 17, cx + 17):
        if pointed:
            pieces.append(
                f'<path d="M{x - 9} {cy + 3} L{x} {cy - 22} L{x + 9} {cy + 3} Z"'
                f' fill="{SAND}" stroke="{OCHRE}" stroke-width="1.6"/>'
            )
        else:
            pieces.append(
                f'<ellipse cx="{x}" cy="{cy - 6}" rx="10" ry="13"'
                f' fill="{SAND}" stroke="{OCHRE}" stroke-width="1.6"/>'
            )
    for x in (cx - 19, cx - 7, cx + 7, cx + 19):
        pieces.append(line(x, cy + 29, x, cy + 40, OCHRE))
    return "\n".join(pieces)


def page(stem: str, title: str, description: str, subtitle: str, body: str) -> None:
    svg = f'''<svg xmlns="http://www.w3.org/2000/svg" width="210mm" height="297mm" viewBox="0 0 210 297" role="img" aria-labelledby="title desc">
<title id="title">{esc(title)}</title><desc id="desc">{esc(description)}</desc>
<rect width="210" height="297" fill="#fff"/>
{label(14, 17, "SubjectNest / Foundation Science / Weeks 29–30", 4, True)}
{label(14, 30, title, 7, True)}
<path d="M14 37 H196" stroke="{TEAL}" stroke-width="1.2"/>
{label(14, 46, subtitle, 3.5)}
{body}
<path d="M14 277 H196" stroke="{LINE}" stroke-width=".6"/>
{label(14, 285, "Point · say · sign · use AAC · direct an adult · choose no touch.", 3.2)}
{label(14, 292, "Original SubjectNest vector · © 2026 NeuroForgeIO Pty Ltd · CC BY 4.0", 2.8)}
</svg>
'''
    source = ROOT / f"{stem}.svg"
    target = ROOT / f"{stem}.pdf"
    source.write_text(svg, encoding="utf-8")
    env = os.environ.copy()
    env["SOURCE_DATE_EPOCH"] = "1790726400"  # 2026-09-30 UTC
    subprocess.run(
        ["rsvg-convert", "--format", "pdf", "--output", str(target), str(source)],
        check=True,
        env=env,
    )


figures = []
for code, title, x, y, shape, caption in (
    ("N1", "PLANT DRAWING", 14, 57, plant(58, 103, False), "ROUNDED LEAF MARKS"),
    ("N2", "PLANT DRAWING", 107, 57, plant(151, 103, True), "POINTED LEAF MARKS"),
    ("N3", "ANIMAL PUPPET", 14, 154, animal(58, 192, True), "POINTED EAR MARKS"),
    ("N4", "ANIMAL PUPPET", 107, 154, animal(151, 192, False), "ROUNDED EAR MARKS"),
):
    figures.extend(
        [
            rect(x, y, 89, 91, MIST),
            label(x + 5, y + 11, f"{code} · {title}", 4.1, True),
            shape,
            label(x + 5, y + 84, caption, 3.6, True),
        ]
    )
figures.extend(
    [
        rect(14, 251, 182, 17, PALE),
        label(
            20,
            262,
            "FICTIONAL PAPER DRAWINGS · DRAWN MARKS, NOT REAL ORGANISMS",
            3.6,
            True,
        ),
    ]
)
page(
    "four-notebook-figures",
    "Four figures in a fictional notebook",
    "Four large original fictional paper figures in two rows. N1 plant drawing has a stem and three broad rounded leaf marks; N2 plant drawing has a stem and three narrow pointed leaf marks; N3 animal puppet drawing has a round body, two pointed triangular ear marks and four legs; N4 animal puppet drawing has a round body, two rounded ear marks and four legs. Bottom edges of plant drawings stop at a line; no roots are shown. These marks are not evidence of real organisms.",
    "Author's invented labels and visible marks are different information.",
    "\n".join(figures),
)


sort = [
    label(17, 58, "SOURCE: FICTIONAL PAPER NOTEBOOK · RULE: OUTER MARK", 3.6, True),
    rect(14, 66, 182, 63, MIST),
    label(21, 78, "UNSORTED · start all four here", 4.4, True),
]
for index, (name, pointed) in enumerate(
    (("N1", False), ("N2", True), ("N3", True), ("N4", False))
):
    x = 22 + index * 44
    sort.append(rect(x, 88, 38, 31, PALE if pointed else SAND))
    sort.append(label(x + 3, 98, name, 4.3, True))
    if pointed:
        sort.append(
            f'<path d="M{x + 20} 101 L{x + 25} 113 L{x + 15} 113 Z" fill="{BLUE}"/>'
        )
    else:
        sort.append(f'<ellipse cx="{x + 20}" cy="108" rx="6" ry="5" fill="{OCHRE}"/>')
sort.extend(
    [
        rect(14, 138, 89, 82, PALE),
        rect(107, 138, 89, 82, SAND),
        label(19, 153, "POINTED OUTER MARK", 3.6, True),
        label(112, 153, "ROUNDED OUTER MARK", 3.4, True),
        label(19, 166, "SEEN IN DRAWING", 3.3),
        label(112, 166, "SEEN IN DRAWING", 3.3),
        rect(14, 230, 182, 38),
        label(20, 244, "COUNTEREXAMPLE + SOURCE", 4.2, True),
        label(20, 259, "Which pictured label breaks a broad claim?", 3.7),
    ]
)
page(
    "pointed-rule-sort",
    "Sort one visible outline rule",
    "A separate unsorted tray contains four broad fixed small symbolic drawing cards labelled N1 through N4. N1 and N4 have rounded symbol outlines; N2 and N3 have pointed triangle symbol outlines. Beneath are two large empty bins POINTED OUTER MARK SEEN IN DRAWING and ROUNDED OUTER MARK SEEN IN DRAWING. A separate empty COUNTEREXAMPLE + SOURCE field follows. No card is pre-sorted.",
    "Name the rule first. Keep picture labels and evidence limits.",
    "\n".join(sort),
)


gate = [label(17, 58, "REAL SOURCE ONLY IF VERIFIED · otherwise MODEL ONLY", 3.8, True)]
for name, x, y in (
    ("P1 · PLANT 1", 14, 67),
    ("P2 · PLANT 2", 107, 67),
    ("A1 · ANIMAL 1", 14, 149),
    ("A2 · ANIMAL 2", 107, 149),
):
    gate.extend(
        [
            rect(x, y, 89, 76, MIST),
            label(x + 5, y + 12, name, 4.2, True),
            rect(x + 7, y + 19, 75, 25),
            label(x + 9, y + 54, "LIVE / PHOTO / ADULT REPORT", 3.0),
            label(x + 9, y + 66, "SOURCE + DATE + ACCESS", 3.5, True),
        ]
    )
gate.extend(
    [
        rect(14, 238, 182, 30, SAND),
        label(20, 250, "STOP OR MODEL ONLY", 4.5, True),
        label(20, 261, "Approval · rights · safe distance · four distinct IDs", 3.3),
    ]
)
page(
    "four-source-gate",
    "Four real sources need a gate",
    "Four large blank panels in two rows labelled P1 PLANT 1, P2 PLANT 2, A1 ANIMAL 1, A2 ANIMAL 2. Each has a blank view area and source prompts LIVE / PHOTO / ADULT REPORT and SOURCE + DATE + ACCESS. Lower stop band says STOP OR MODEL ONLY; approval, rights, safe distance and four distinct IDs are needed. No real source or result is prefilled.",
    "Adult confirms approval, provenance, access and safety first.",
    "\n".join(gate),
)


log = [
    label(17, 57, "PLANT PAIR · two distinct subjects", 4, True),
    label(17, 159, "ANIMAL PAIR · two distinct subjects", 4, True),
]
for name, x, y in (
    ("P1", 14, 65),
    ("P2", 107, 65),
    ("A1", 14, 167),
    ("A2", 107, 167),
):
    log.extend(
        [
            rect(x, y, 89, 87, MIST),
            label(x + 5, y + 12, name, 4.5, True),
            rect(x + 7, y + 18, 75, 37),
            label(x + 9, y + 65, "VISIBLE / NOT VISIBLE", 3.2, True),
            label(x + 9, y + 75, "UNCLEAR · SOURCE + DATE", 3.2),
        ]
    )
log.extend(
    [
        rect(14, 258, 182, 10, PALE),
        label(20, 265, "NO FEATURE OR ORGANISM IS PREFILLED", 3.4, True),
    ]
)
page(
    "two-pair-log",
    "Two pairs; four blank feature views",
    "A paired plant row P1 and P2 and animal row A1 and A2, each with a large blank drawing/record field and separate VISIBLE / NOT VISIBLE, UNCLEAR, SOURCE + DATE prompts. No feature or organism is prefilled; only distinct IDs are printed.",
    "Record exact feature and the source of each view separately.",
    "\n".join(log),
)


revision = [
    rect(14, 59, 182, 56, PALE),
    label(21, 74, "1 · FIRST RULE", 5.1, True),
    label(21, 88, "What feature? Which view? How wide is the claim?", 3.9),
    rect(14, 135, 182, 56, SAND),
    label(21, 150, "2 · COUNTEREXAMPLE + SOURCE", 4.8, True),
    label(21, 164, "Which ID and exact mark do not fit?", 3.9),
    rect(14, 211, 182, 56, MIST),
    label(21, 226, "3 · SMALLER RULE / QUESTION", 4.8, True),
    label(21, 240, "What can the source support now?", 3.9),
    f'<path d="M105 119 V130 M101 126 L105 131 L109 126" fill="none" stroke="{TEAL}" stroke-width="1.8"/>',
    f'<path d="M105 195 V206 M101 202 L105 207 L109 202" fill="none" stroke="{TEAL}" stroke-width="1.8"/>',
]
page(
    "revise-rule-board",
    "Repair a broad feature rule",
    "Three large blank fields in reading order: 1 FIRST RULE, 2 COUNTEREXAMPLE + SOURCE, 3 SMALLER RULE / QUESTION. Broad arrows connect the fields. Each has a prompt but no answer is prefilled.",
    "A contrary example can make an every-claim smaller.",
    "\n".join(revision),
)


share_labels = (
    "SOURCE TYPE + DATE",
    "WHAT I NOTICE",
    "MY RULE",
    "COUNTEREXAMPLE",
    "WHAT I CAN SAY NOW",
    "LISTENER RESPONSE OR NONE",
)
share = []
for index, name in enumerate(share_labels):
    y = 57 + index * 35
    share.extend(
        [
            rect(14, y, 182, 30, PALE if index % 2 == 0 else MIST),
            label(20, y + 11, f"{index + 1} · {name}", 4.0, True),
            line(20, y + 23, 188, y + 23, LINE),
        ]
    )
page(
    "feature-share-strip",
    "Share a bounded feature idea",
    "Six large blank horizontal strips in order SOURCE TYPE + DATE, WHAT I NOTICE, MY RULE, COUNTEREXAMPLE, WHAT I CAN SAY NOW, LISTENER RESPONSE OR NONE. Each has a writing line and no claim or listener response is prefilled.",
    "A model, photograph and actual view need different source labels.",
    "\n".join(share),
)

print("Rendered six original SVG/PDF A4 pairs")
