#!/usr/bin/env python3
"""Generate four original A4 Design concept aids as SVG and PDF."""
from __future__ import annotations

import argparse
import os
import subprocess
from pathlib import Path
from xml.sax.saxutils import escape

HERE = Path(__file__).resolve().parent
INK, BLUE, MUTED, PALE = '#1b2932', '#255574', '#455866', '#edf3f8'


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


def box(x: int, y: int, w: int, h: int, fill: str = 'white') -> str:
    return (f'<rect x="{x}" y="{y}" width="{w}" height="{h}" rx="9" '
            f'fill="{fill}" stroke="{BLUE}" stroke-width="2"/>')


def rule(x1: int, y1: int, x2: int, y2: int) -> str:
    return f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" stroke="{MUTED}" stroke-width="2"/>'


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 794 1123" role="img">',
        f'<title>{escape(title)}</title>', f'<desc>{escape(description)}</desc>',
        '<rect width="794" height="1123" fill="white"/>',
        box(32, 25, 730, 105, PALE),
        text(50, 62, 'SUBJECTNEST / YEAR 11 / DESIGN', 16, True, BLUE),
        text(50, 106, title, 28, True), *body,
        rule(34, 1072, 760, 1072),
        text(37, 1101, 'Original A4 aid · full text and tactile route supplied', 13, colour=MUTED),
        '</svg>', ''
    ])


def evidence() -> str:
    body = [text(43, 165, 'Keep each claim at its actual source level.', 19, True)]
    rows = [
        ('1  SCRIPTED BRIEF FACT', 'Card B has two side-room entrances marked L and R.',
         'This is a fictional card fact, not a real interview.'),
        ('2  CAUTIOUS INFERENCE', 'Full-word room names might reduce ambiguity.',
         'A possible design idea; no use outcome is known.'),
        ('3  UNSUPPORTED CLAIM', 'Every performer will find the rooms quickly.',
         'No timing, user sample or real-site test is supplied.'),
        ('4  NEXT EVIDENCE', 'Ask a consented stakeholder to try a prototype.',
         'A proposed check is not a reported result.'),
    ]
    for i, (head, line1, line2) in enumerate(rows):
        y = 207 + 205 * i
        body += [box(43, y, 708, 184, PALE if i % 2 == 0 else 'white'),
                 text(61, y + 43, head, 18, True, BLUE),
                 text(61, y + 94, line1, 16), text(61, y + 137, line2, 16)]
    return page('Evidence ladder',
                'Four numbered evidence levels: fictional stated fact, possible inference, unsupported claim, and next evidence. Full wording makes all distinctions independently of colour.', body)


def problem() -> str:
    body = [text(43, 163, 'Build a problem from a source, not from a preferred solution.', 18, True)]
    rows = [
        ('WHO / TASK', 'Doorway reader needs to find either side room.'),
        ('PRESENT DESIGN', 'Current Card B plan gives only L and R.'),
        ('BRIEF CONSTRAINTS', 'A4 portrait; black print; arrows must match corridor.'),
        ('UNKNOWN', 'Real sightline and reading distance are not supplied.'),
    ]
    for i, (head, detail) in enumerate(rows):
        y = 197 + 150 * i
        body += [box(43, y, 708, 130, PALE if i % 2 == 0 else 'white'),
                 text(61, y + 42, f'{i + 1}  {head}', 18, True, BLUE),
                 text(61, y + 88, detail, 16)]
    body += [box(43, 813, 708, 119, PALE),
             text(61, 851, 'PAPER-CHECKABLE CRITERION', 17, True, BLUE),
             text(61, 894, 'Both destinations have full-word labels on the draft.', 16),
             text(43, 992, 'A labelled draft is not proof of real-world wayfinding.', 17, True)]
    return page('Problem to criteria',
                'Four full-word problem ingredients for fictional Card B: who and task, present design, brief constraints and unknown. A final box gives one paper-checkable full-word-label criterion with a real-world evidence caution.', body)


def ideas() -> str:
    body = [text(43, 165, 'Change the information system, then test against criteria.', 18, True)]
    rows = [
        ('EXPLORE', 'Card E: visitor needs the route; actual use unknown.'),
        ('IDEA A', 'Room numbers lead; full names appear in a key.'),
        ('IDEA B', 'Full room names lead; numbers are secondary.'),
        ('COMPARE', 'Both must preserve Entrance → 1 → 2 → 3.'),
        ('REFINE', 'Move a label or legend, then annotate why.'),
    ]
    for i, (head, detail) in enumerate(rows):
        y = 194 + 159 * i
        body += [box(44, y, 706, 139, PALE if i % 2 == 0 else 'white'),
                 text(61, y + 44, f'{i + 1}  {head}', 18, True, BLUE),
                 text(61, y + 94, detail, 16)]
    body += [text(43, 1030, 'Not a copy of a named design-process model.', 15, colour=MUTED)]
    return page('Idea loop',
                'Five ordered full-word steps: explore a fictional need, make room-number-led Idea A, make room-name-led Idea B, compare both to the stated corridor, and refine a visible label or legend. This is an original lesson organiser, not a copy of a named process model.', body)


def decision() -> str:
    body = [text(43, 164, 'Use visible features to justify a possible revision.', 18, True)]
    body += [box(43, 193, 708, 91, PALE),
             text(60, 230, 'CARD F · TWO PAPER OPTIONS', 18, True, BLUE),
             text(60, 262, 'X: pictures, no words. Y: words, tiny step numbers.', 16)]
    cells = [
        (43, 311, 'CRITERION 1', 'Exactly three numbered, worded steps.'),
        (404, 311, 'CRITERION 2', '“Paper game only” is visible.'),
        (43, 488, 'X · STRENGTH / LIMIT', 'Picture sequence / missing words.'),
        (404, 488, 'Y · STRENGTH / LIMIT', 'Words present / tiny numbers.'),
    ]
    for x, y, head, detail in cells:
        body += [box(x, y, 347, 152, PALE if x == 43 else 'white'),
                 text(x + 14, y + 44, head, 16, True, BLUE),
                 text(x + 14, y + 94, detail, 15)]
    body += [box(43, 688, 708, 137, PALE),
             text(60, 733, 'REVISE THE LOW-FIDELITY IDEA', 17, True, BLUE),
             text(60, 779, 'Pair full-word steps with large 1 / 2 / 3.', 16),
             box(43, 848, 708, 142),
             text(60, 895, 'EVIDENCE STILL NEEDED', 17, True, BLUE),
             text(60, 941, 'A consented reader check before a usability claim.', 16)]
    return page('Decision grid',
                'A comparison of fictional Card F: two checkable criteria, strengths and limitations of options X and Y, a low-fidelity combined revision and an explicit need for future consented reader evidence. All boxes are labelled in full words.', body)


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument('--write', action='store_true')
    if not parser.parse_args().write:
        parser.error('pass --write to regenerate all four original A4 aid pairs')
    env = {**os.environ, 'SOURCE_DATE_EPOCH': '1790640000'}
    for stem, maker in (('evidence-ladder', evidence),
                        ('problem-criteria', problem),
                        ('idea-loop', ideas),
                        ('decision-grid', decision)):
        svg = HERE / f'{stem}.svg'
        pdf = HERE / f'{stem}.pdf'
        svg.write_text(maker(), encoding='utf-8')
        subprocess.run(['rsvg-convert', '--format', 'pdf', '--output', str(pdf), str(svg)],
                       check=True, env=env)
        print(f'generated {svg.name} + {pdf.name}')


if __name__ == '__main__':
    main()
