"""Writes a notice of a rate change claiming the second part of the archival
standard, at level A.

Level A is the one that asks the page to say what it is made of: which run
of words is the heading, which are paragraphs, and which marks are no part
of the reading at all. A screen reader follows that tree; a page that only
looks right has nothing for it to follow.

The rule and the page number at the foot are marked as what they are —
furniture, not text — so nothing reading the document aloud says them.

Whether the result really is PDF/A-2A is not for us to say. `veraPDF` says,
and `scripts/check_archival.sh` asks it.

The words of the page and of its metadata are held in `Words`, once per language,
and `HQF_PDF_LANG` picks which set the record is written in. The tree names the same
language, so a screen reader reads the page in it.

Usage: `python examples/write_pdfa2a.py [out.pdf] [font.ttf]`
       `HQF_PDF_LANG=fr python examples/write_pdfa2a.py`
"""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

# When the record was drawn up. The library never reads the clock, so the caller says
# when.
ISSUED = "2026-04-02T08:00:00+02:00"

# Who the file says wrote it, and what produced it, in every language alike.
AUTHOR = "HQF Development"
PRODUCER = "hqf-pdf-server"

@dataclass(frozen=True)
class Words:
    """Every word the notice is written in, in one language."""

    # What the file says it is, in its metadata.
    title: str
    # What the file says it is about, in its metadata.
    subject: str
    # The language the tree says the page is written in, which a screen reader reads it
    # in.
    lang: str
    # The heading, tagged `H1`.
    heading: str
    # The two paragraphs under the heading, each tagged `P`.
    paragraphs: tuple[str, str]
    # The page number at the foot, marked as furniture.
    foot: str


# The notice in English.
ENGLISH = Words(
    title="Notice of rate change, April 2026",
    subject="A record that says what it is made of, and keeps its looks for good",
    lang="en-GB",
    heading="Notice of rate change",
    paragraphs=(
        (
            "This notice claims the second part of the archival standard at its "
            "highest level. On top of the promise that the page keeps its looks, and "
            "on top of every mark of ink saying which letter it stands for, the file "
            "says what it is made of: this line is a heading, that block is a "
            "paragraph, and the rule across the foot is furniture that says nothing at "
            "all.\nSomething reading the notice aloud follows the order the tree gives "
            "rather than the order the ink was laid down in, and reading software on a "
            "telephone has something to reflow."
        ),
        (
            "The new rate applies to every order acknowledged on or after 1 May 2026. "
            "Orders already acknowledged stand at the rate they were acknowledged at."
        ),
    ),
    foot="Page 1 of 1",
)


# The notice in French.
FRENCH = Words(
    title="Avis de changement de tarif, avril 2026",
    subject=(
        "Un document qui dit de quoi il est fait et garde durablement son apparence"
    ),
    lang="fr-FR",
    heading="Avis de changement de tarif",
    paragraphs=(
        (
            ("Cet avis déclare suivre la deuxième partie de la norme d’archivage, à "
             "son niveau le plus exigeant. En plus de la promesse que la page garde "
             "son apparence, et de chaque trace d’encre qui dit quelle lettre elle "
             "représente, le fichier dit de quoi il est fait : cette ligne est un "
             "titre, ce bloc est un paragraphe, et le filet en pied de page est un "
             "ornement qui ne dit rien.\nUn logiciel qui lit l’avis à voix haute suit "
             "l’ordre que donne l’arbre de structure, et non celui dans lequel l’encre"
             " a été posée ; sur un téléphone, une application de lecture a de quoi "
             "recomposer le texte.")
        ),
        (
            "Le nouveau tarif s’applique à toute commande confirmée à partir du 1er "
            "mai 2026. Les commandes déjà confirmées restent au tarif en vigueur lors "
            "de leur confirmation."
        ),
    ),
    foot="Page 1 sur 1",
)


# The notice in German.
GERMAN = Words(
    title="Mitteilung über eine Preisänderung, April 2026",
    subject=(
        ("Ein Dokument, das angibt, woraus es besteht, und sein Aussehen dauerhaft "
         "behält")
    ),
    lang="de-DE",
    heading="Mitteilung über eine Preisänderung",
    paragraphs=(
        (
            ("Diese Mitteilung erklärt Konformität mit dem zweiten Teil der "
             "Archivnorm, auf dessen höchster Stufe. Zugesagt ist, dass die Seite ihr "
             "Aussehen behält und jede Druckspur angibt, für welchen Buchstaben sie "
             "steht; darüber hinaus gibt die Datei an, woraus sie besteht: Diese Zeile"
             " ist eine Überschrift, jener Block ein Absatz, und die Linie am Fuß ist "
             "Zierrat ohne jede Aussage.\nEin Vorleseprogramm folgt der Ordnung des "
             "Strukturbaums statt der Reihenfolge, in der die Tinte aufgetragen wurde,"
             " und eine Lese-App auf dem Smartphone kann den Text neu umbrechen.")
        ),
        (
            "Der neue Preis gilt für jede Bestellung, die ab dem 1. Mai 2026 bestätigt "
            "wird. Bereits bestätigte Bestellungen bleiben bei dem Preis, zu dem sie "
            "bestätigt wurden."
        ),
    ),
    foot="Seite 1 von 1",
)


# The notice in Spanish.
SPANISH = Words(
    title="Aviso de cambio de tarifa, abril de 2026",
    subject=(
        "Un documento que dice de qué está hecho y conserva su aspecto para siempre"
    ),
    lang="es-ES",
    heading="Aviso de cambio de tarifa",
    paragraphs=(
        (
            ("Este aviso declara cumplir la segunda parte de la norma de archivo en su"
             " nivel más alto. Además de prometer que la página conserva su aspecto y "
             "que cada trazo de tinta dice qué letra representa, el fichero dice de "
             "qué está hecho: esta línea es un título, ese bloque es un párrafo y el "
             "filete del pie es un adorno que no dice nada.\nUn programa que lea el "
             "aviso en voz alta sigue el orden que da el árbol, no el orden en que se "
             "puso la tinta, y una aplicación de lectura en el móvil tiene con qué "
             "recomponer el texto.")
        ),
        (
            ("La nueva tarifa se aplica a todo pedido confirmado a partir del 1 de "
             "mayo de 2026. Los pedidos ya confirmados mantienen la tarifa vigente en "
             "el momento de su confirmación.")
        ),
    ),
    foot="Página 1 de 1",
)


# The notice in Italian.
ITALIAN = Words(
    title="Avviso di variazione tariffaria, aprile 2026",
    subject=(
        "Un documento che dice di che cosa è fatto e conserva il suo aspetto per sempre"
    ),
    lang="it-IT",
    heading="Avviso di variazione tariffaria",
    paragraphs=(
        (
            ("Quest’avviso dichiara di rispettare la seconda parte della norma di "
             "archiviazione al suo livello più alto. Oltre alla promessa che la pagina"
             " conservi il suo aspetto e che ogni segno d’inchiostro dica quale "
             "lettera rappresenta, il file dice di che cosa è fatto: questa riga è un "
             "titolo, quel blocco è un paragrafo e il filetto a piè di pagina è un "
             "ornamento che non dice nulla.\nUna sintesi vocale legge l’avviso "
             "seguendo l’ordine dato dall’albero della struttura, non quello in cui è "
             "stato steso l’inchiostro, e un’app di lettura sul telefono può "
             "reimpaginare il testo.")
        ),
        (
            "La nuova tariffa si applica a ogni ordine confermato dal 1° maggio 2026 "
            "in poi. Gli ordini già confermati restano alla tariffa in vigore al "
            "momento della conferma."
        ),
    ),
    foot="Pagina 1 di 1",
)


# The notice in Portuguese.
PORTUGUESE = Words(
    title="Aviso de alteração de tarifa, abril de 2026",
    subject="Um documento que diz de que é feito e mantém o seu aspeto para sempre",
    lang="pt-PT",
    heading="Aviso de alteração de tarifa",
    paragraphs=(
        (
            ("Este aviso declara cumprir a segunda parte da norma de arquivo, no seu "
             "nível mais alto. Além da promessa de que a página mantém o seu aspeto, e"
             " de que cada traço de tinta diz que letra representa, o ficheiro diz de "
             "que é feito: esta linha é um título, aquele bloco é um parágrafo e o "
             "filete do rodapé é um ornamento que nada diz.\nUm programa que lê o "
             "aviso em voz alta segue a ordem dada pela árvore, e não a ordem em que a"
             " tinta foi posta, e uma aplicação de leitura no telemóvel tem com que "
             "recompor o texto.")
        ),
        (
            "A nova tarifa aplica-se a todas as encomendas confirmadas a partir de 1 "
            "de maio de 2026. As encomendas já confirmadas mantêm a tarifa em vigor à "
            "data da confirmação."
        ),
    ),
    foot="Página 1 de 1",
)


# The notice in Dutch.
DUTCH = Words(
    title="Bericht van tariefwijziging, april 2026",
    subject=(
        "Een document dat zegt waaruit het bestaat en zijn uiterlijk voorgoed behoudt"
    ),
    lang="nl-NL",
    heading="Bericht van tariefwijziging",
    paragraphs=(
        (
            ("Dit bericht verklaart te voldoen aan het tweede deel van de archiefnorm,"
             " op het hoogste niveau. Naast de belofte dat de pagina haar uiterlijk "
             "behoudt en dat elk inktspoor aangeeft voor welke letter het staat, "
             "vertelt het bestand waaruit het bestaat: deze regel is een kop, dat blok"
             " is een alinea en de lijn onderaan is opsmuk die niets zegt.\nEen "
             "voorleesprogramma houdt de volgorde van de boom aan en niet die waarin "
             "de inkt is neergezet, en een leesapp op een telefoon kan de tekst "
             "herschikken.")
        ),
        (
            ("Het nieuwe tarief geldt voor elke bestelling die vanaf 1 mei 2026 wordt "
             "bevestigd. Voor al bevestigde bestellingen blijft het tarief gelden "
             "waartegen ze zijn bevestigd.")
        ),
    ),
    foot="Pagina 1 van 1",
)

# Every language the example is written in. A language is added by writing its own set
# of words and naming it here.
WORDS = {
    _language.ENGLISH: ENGLISH,
    _language.FRENCH: FRENCH,
    _language.GERMAN: GERMAN,
    _language.SPANISH: SPANISH,
    _language.ITALIAN: ITALIAN,
    _language.PORTUGUESE: PORTUGUESE,
    _language.DUTCH: DUTCH,
}


# Where each block of what the page says goes, in the order it is read: the tag it
# stands under, the size it is set at, the box it goes in, and the leading where it
# states one. The blocks say the heading and the two paragraphs of `Words`, in that
# order.
READ = (
    ("H1", 20.0, 72.0, 782.0, 451.0, None),
    ("P", 11.0, 72.0, 730.0, 451.0, 16.0),
    ("P", 11.0, 72.0, 580.0, 451.0, 16.0),
)

# The rule at the foot of the page, and the box of the line under it. Neither is part of
# the reading, so both are marked as furniture.
RULE = (72.0, 70.0, 451.0, 1.0)
FOOT = (9.0, 72.0, 68.0, 451.0)


def foot_of_page(
    content: hqf_pdf.Content, handle: hqf_pdf.FontHandle, text: str
) -> None:
    """Draws the rule and the page number, both marked as furniture so that
    nothing reading the document aloud says them."""
    left, low, width, height = RULE
    content.begin_artifact(hqf_pdf.Artifact.Footer)
    content.save_state()
    # Rust says this as `set_fill_rgb`; Python takes the colour itself.
    content.set_fill(hqf_pdf.Rgb(0.0, 0.0, 0.0))
    content.rect(left, low, width, height)
    content.fill()
    content.restore_state()
    content.end_marked()

    size, left, top, width = FOOT
    flow = hqf_pdf.TextFlow(
        handle, size, first_baseline=hqf_pdf.FirstBaseline.points(0.0)
    )
    lines = flow.break_lines(text, width)
    content.begin_artifact(hqf_pdf.Artifact.Footer)
    # The Python side opens and closes the text object itself.
    flow.draw(content, lines, left, top, width)
    content.end_marked()


def sheet(handle: hqf_pdf.FontHandle, words: Words):
    """The one page, and the branch of the tree that says what is on it."""
    content = hqf_pdf.Content()
    marks = hqf_pdf.Marks()
    branches = []

    said = (words.heading, words.paragraphs[0], words.paragraphs[1])
    for (tag, size, left, top, width, leading), text in zip(READ, said):
        settings = {} if leading is None else {"leading": leading}
        flow = hqf_pdf.TextFlow(
            handle,
            size,
            first_baseline=hqf_pdf.FirstBaseline.points(0.0),
            **settings,
        )
        lines = flow.break_lines(text, width)
        runs = flow.draw_tagged(content, lines, left, top, width, tag, marks)
        branches.append(
            hqf_pdf.StructElement(tag, children=[(0, run) for run in runs])
        )

    foot_of_page(content, handle, words.foot)

    page = hqf_pdf.Page.a4()
    page.set_content(content)
    return page, hqf_pdf.StructElement("Sect", children=branches)


def main() -> None:
    """Writes the file, and says where it went."""
    language = _language.from_environment()
    words = _language.words_of(WORDS, language)

    # A named file is written as named; the default one carries the language, so the
    # languages do not overwrite each other in `tmp/`.
    out = _out.output_path(Path(_language.file_name("pdfa2a.pdf", language)).stem)

    document = hqf_pdf.Document()
    document.set_license(_licence.licensed())
    document.set_conformance(hqf_pdf.PdfA.A2A)
    document.set_metadata(
        hqf_pdf.Metadata(
            title=words.title,
            author=AUTHOR,
            subject=words.subject,
            producer=PRODUCER,
            created=ISSUED,
        )
    )

    handle = document.add_font(hqf_pdf.Font.from_path(_out.font_path()))
    page, sect = sheet(handle, words)
    document.add_page(page)
    document.set_structure(
        hqf_pdf.StructureTree(
            children=[hqf_pdf.StructElement("Document", children=[sect])],
            lang=words.lang,
        )
    )

    data = document.to_bytes()
    out.parent.mkdir(parents=True, exist_ok=True)
    out.write_bytes(data)
    print(f"wrote {out}: {len(data)} bytes")


if __name__ == "__main__":
    main()
