# SPDX-License-Identifier: Apache-2.0
"""Deterministically render eight original A4 Visual Arts W11-12 aids."""

from __future__ import annotations

import subprocess
from io import BytesIO
from pathlib import Path
from xml.sax.saxutils import escape

from pypdf import PdfReader, PdfWriter

HERE = Path(__file__).resolve().parent
INK = "#21334b"
BLUE = "#e9f3fb"
SAND = "#fff1df"
GREEN = "#eaf5e9"
GREY = "#f3f5f7"
PURPLE = "#f0ebfa"


def word(x: int, y: int, value: str, size: int = 17, *, bold: bool = False) -> str:
    return f'<text x="{x}" y="{y}" fill="{INK}" font-family="DejaVu Sans" font-size="{size}" font-weight="{700 if bold else 400}">{escape(value)}</text>'


def box(x: int, y: int, w: int, h: int, fill: str = "white", *, name: str = "", radius: int = 8) -> str:
    data = f' data-part="{escape(name)}"' if name else ""
    return f'<rect x="{x}" y="{y}" width="{w}" height="{h}" rx="{radius}" fill="{fill}" stroke="{INK}" stroke-width="2"{data}/>'


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


def circle(cx: int, cy: int, r: int, fill: str = "white", width: int = 3) -> str:
    return f'<circle cx="{cx}" cy="{cy}" r="{r}" fill="{fill}" stroke="{INK}" stroke-width="{width}"/>'


def page(title: str, description: str, body: str) -> str:
    return (
        '<svg xmlns="http://www.w3.org/2000/svg" width="210mm" height="297mm" '
        'viewBox="0 0 794 1123" role="img">'
        f'<title>{escape(title)}</title><desc>{escape(description)}</desc>'
        '<rect width="794" height="1123" fill="white"/>'
        f'{word(42, 63, title, 27, bold=True)}'
        f'{word(42, 100, description, 13)}'
        f'{body}{line(42, 1043, 752, 1043)}'
        f'{word(42, 1076, "SubjectNest original Foundation Visual Arts W11-12 · CC BY 4.0", 10)}'
        '</svg>\n'
    )


def ten_mark_layouts() -> str:
    body = word(42, 146, "SAME TEN SUPPLIED SQUARE MARKS", 18, bold=True)
    for x, label, fill in ((42, "LOW / WIDE WHOLE", BLUE), (404, "TALL / NARROW WHOLE", SAND)):
        body += box(x, 184, 348, 583, fill, name=label)
        body += word(x + 18, 228, label, 18, bold=True)
    for row in range(2):
        for col in range(5):
            body += box(72 + col * 58, 384 + row * 58, 45, 45, "white", name="supplied square", radius=0)
    for row in range(5):
        for col in range(2):
            body += box(520 + col * 58, 283 + row * 58, 45, 45, "white", name="supplied square", radius=0)
    body += box(42, 800, 710, 194, GREY)
    body += word(60, 850, "The given number stays; the whole outline changes.", 17, bold=True)
    body += word(60, 901, "What image might YOU make by placing the same marks?", 16)
    body += word(60, 951, "Counting evidence belongs in the separate maths lesson.", 14)
    return page("Same marks, new whole", "Ten broad squares make one wide band or one tall band.", body)


def one_large_mark() -> str:
    body = word(42, 145, "TWO ADULT-MADE SIZE IDEAS", 19, bold=True)
    for x, title, fill in ((42, "IDEA A · ONE LARGE", BLUE), (404, "IDEA B · ALL EQUAL", GREEN)):
        body += box(x, 184, 348, 572, fill, name=title)
        body += word(x + 17, 230, title, 17, bold=True)
    centres = [(x, y) for y in (335, 425, 515) for x in (105, 211, 317)]
    for i, (x, y) in enumerate(centres):
        body += circle(x, y, 18 if i != 4 else 47, "white")
    body += circle(211, 621, 18, "white")
    for y in (330, 415):
        for x in (466, 541, 616, 691, 729):
            if x == 729:
                continue
            body += circle(x, y, 20, "white")
    body += circle(504, 512, 20, "white") + circle(579, 512, 20, "white")
    body += box(42, 797, 710, 199, GREY)
    body += word(60, 849, "Which part would you make larger, smaller or equal?", 17, bold=True)
    body += word(60, 901, "Name a visual purpose; no viewer has been tested.", 16)
    body += word(60, 952, "Ten marks are supplied, not a count test here.", 14)
    return page("One part can be large", "Adult example of one large among ten discs versus equal-size discs.", body)


def arch(x: int, y: int, w: int = 53) -> str:
    return f'<path d="M{x} {y+70} V{y+35} A{w//2} {w//2} 0 0 1 {x+w} {y+35} V{y+70}" fill="none" stroke="{INK}" stroke-width="7"/>'


def turning_page() -> str:
    body = word(42, 144, "SAME THREE ARCH MARKS · NEW PAGE DIRECTION", 17, bold=True)
    body += box(42, 189, 299, 695, BLUE, name="TALL PAGE")
    body += word(62, 234, "TALL PAGE", 19, bold=True)
    body += box(384, 189, 368, 489, SAND, name="WIDE PAGE")
    body += word(403, 234, "WIDE PAGE", 19, bold=True)
    for x in (93, 163, 233):
        body += arch(x, 428)
    for x in (420, 524, 628):
        body += arch(x, 400)
    body += box(384, 714, 368, 170, GREY)
    body += word(403, 759, "What changes around", 16, bold=True)
    body += word(403, 792, "the marks?", 16, bold=True)
    body += word(403, 841, "Choose a reason.", 15)
    body += word(42, 960, "Turning the idea needs no body movement or page handling.", 14)
    return page("Tall page or wide page?", "Same three broad arches inside a tall and a wide frame.", body)


def composition_canvas() -> str:
    body = box(42, 158, 710, 693, "white", name="blank original art area")
    body += word(62, 204, "MY WHOLE PICTURE", 20, bold=True)
    body += word(62, 251, "My source: FICTIONAL / ACTUAL + DATE", 16)
    body += line(62, 286, 727, 286)
    body += word(62, 340, "Place, size or page direction I chose:", 16)
    body += line(62, 376, 727, 376)
    body += word(62, 794, "Make, direct or describe the image in this area.", 15)
    body += box(42, 885, 710, 110, GREEN, name="reason and maker")
    body += word(60, 930, "WHY?", 17, bold=True)
    body += line(128, 936, 727, 936)
    body += word(60, 977, "Actual marker/placer: __________  or PLAN ONLY", 14)
    return page("My composition canvas", "Large blank art area for placement, size or page direction and reason.", body)


def two_maker_page() -> str:
    body = word(42, 145, "FICTIONAL OFFERS · NO REAL PARTNER YET", 18, bold=True)
    body += box(42, 184, 348, 321, BLUE, name="Ari offer")
    body += word(60, 226, "ARI OFFERS ONE ARCH", 17, bold=True)
    body += arch(171, 312, 78)
    body += box(404, 184, 348, 321, SAND, name="Bo offer")
    body += word(422, 226, "BO OFFERS THREE BARS", 17, bold=True)
    for y in (320, 374, 428):
        body += line(513, y, 665, y, 12)
    body += box(42, 548, 710, 319, "white", name="blank shared page")
    body += word(60, 591, "OUR POSSIBLE WHOLE PAGE (blank)", 18, bold=True)
    body += word(60, 828, "What could the two different offers make together?", 16)
    body += box(42, 897, 710, 98, GREY)
    body += word(60, 937, "Ask before moving a real maker's mark.", 16, bold=True)
    body += word(60, 974, "Fictional offers are not actual permission.", 14)
    return page("Two makers, one possible page", "Fictional Ari arch and Bo three bars, with a blank shared area.", body)


def art_maker_record() -> str:
    entries = (
        ("MY WHOLE ART IDEA", "Placement / size / page direction and why.", BLUE),
        ("SOURCE AND ACTUAL MAKER", "FICTION / REAL + DATE; who marked/placed or PLAN ONLY?", SAND),
        ("REAL SHARED PAGE?", "Both makers agreed and chose marks? Or SOLO / FICTION?", GREEN),
        ("ACTUAL WILLING VIEWER", "Who truly saw original work? Reply or NOT SHARED.", GREY),
    )
    body = word(42, 145, "Do not turn a fictional plan into an actual event.", 17, bold=True)
    for i, (title, prompt, fill) in enumerate(entries):
        y = 185 + i * 201
        body += box(42, y, 710, 175, fill, name=title)
        body += word(60, y + 42, title, 18, bold=True)
        body += word(60, y + 84, prompt, 15)
        body += line(60, y + 145, 733, y + 145)
    return page("Art and maker record", "Four blank fields for art decision, source/maker, real co-make and viewer.", body)


def check_s() -> str:
    body = word(42, 146, "CHECK S · FICTIONAL PAPER ORCHARD", 18, bold=True)
    body += box(42, 181, 710, 307, GREEN, name="ten supplied round marks")
    for row in range(2):
        for col in range(5):
            body += circle(151 + col * 122, 296 + row * 86, 31, "white")
    body += word(67, 461, "TEN SUPPLIED PLAIN ROUND MARKS · NO REAL FRUIT", 15, bold=True)
    body += word(42, 534, "Two different whole pictures from the same given set.", 16)
    for y, title in ((558, "IDEA 1"), (765, "IDEA 2")):
        body += box(42, y, 710, 179, "white", name=title)
        body += word(60, y + 43, title, 18, bold=True)
    body += word(42, 996, "Choose one. What visible choice made it your picture?", 14)
    return page("Check S: Paper orchard", "Ten supplied round marks above two blank composition spaces.", body)


def check_t() -> str:
    body = word(42, 145, "CHECK T · FICTIONAL PUPPET WORKSHOP", 17, bold=True)
    body += box(42, 181, 710, 302, PURPLE, name="fictional attributed source")
    body += word(62, 232, "MAKER A: ONE WIDE V", 17, bold=True)
    body += f'<path d="M120 290 L206 414 L292 290" fill="none" stroke="{INK}" stroke-width="10"/>'
    body += word(400, 232, "MAKER B: TWO ROUND BUTTONS", 16, bold=True)
    body += circle(497, 346, 37, "white") + circle(629, 346, 37, "white")
    body += word(42, 527, "Two possible shared pictures; keep each offer named.", 16)
    for y, title in ((551, "IDEA 1"), (762, "IDEA 2")):
        body += box(42, y, 710, 184, "white", name=title)
        body += word(60, y + 43, title, 18, bold=True)
    body += word(42, 995, "Choose one and say why. Real makers would need agreement.", 14)
    return page("Check T: Puppet workshop", "Fictional A wide V and B two round buttons above blank ideas.", body)


PAGES = {
    "ten-mark-layouts": ten_mark_layouts,
    "one-large-mark": one_large_mark,
    "turning-page": turning_page,
    "composition-canvas": composition_canvas,
    "two-maker-page": two_maker_page,
    "art-maker-record": art_maker_record,
    "check-s-orchard": check_s,
    "check-t-puppet": check_t,
}


def main() -> None:
    for stem, make in PAGES.items():
        svg = HERE / f"{stem}.svg"
        pdf = HERE / f"{stem}.pdf"
        svg.write_text(make(), encoding="utf-8")
        rendered = subprocess.run(["rsvg-convert", "-f", "pdf", str(svg)], check=True, capture_output=True)
        writer = PdfWriter()
        writer.append_pages_from_reader(PdfReader(BytesIO(rendered.stdout)))
        writer.add_metadata({
            "/Title": stem,
            "/Author": "SubjectNest by NeuroForgeIO Pty Ltd",
            "/CreationDate": "D:20260930000000+10'00'",
            "/ModDate": "D:20260930000000+10'00'",
        })
        writer.write(str(pdf))
        print(f"WROTE {stem}.svg + .pdf")


if __name__ == "__main__":
    main()
