"""Writes the minutes of a works council claiming the second part of the
archival standard, at level B.

Level B promises one thing and one thing only: the page will look the same
in thirty years as it does today. Every face the page draws with is carried
inside the file, so nothing on it waits on a font that happens to be
installed. It says nothing about the words being searchable — that is level
U — nor about the page saying what it is made of, which is level A.

Whether the result really is PDF/A-2B 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.

Usage: `python examples/write_pdfa2b.py [out.pdf] [font.ttf]`
       `HQF_PDF_LANG=fr python examples/write_pdfa2b.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 — a document that
# stamped itself with the time would be a different file on every build — so the caller
# says when.
ISSUED = "2026-03-12T18:05:00+01: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 minutes 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 heading at the top of the page.
    heading: str
    # The line under the heading saying what the record is filed under.
    reference: str
    # What the page says of the part it claims, in two runs set end to end with a space
    # between them.
    body: tuple[str, str]
    # What each line of the record asks, one a line.
    labels: str
    # What each line of the record answers, beside its label.
    answers: str


# The minutes in English.
ENGLISH = Words(
    title="Minutes of the works council, 12 March 2026",
    subject="A record kept for thirty years, carrying nothing but itself",
    heading="Minutes of the works council",
    reference="Thursday 12 March 2026, 17:00, meeting room 2",
    body=(
        (
            "This sheet claims the second part of the archival standard, at the level "
            "that promises one thing and one thing only: the page will look the same "
            "in thirty years as it does today. Every face it draws with is carried "
            "inside the file, every colour says what it means, and nothing on the page "
            "waits on a program, a network or a font that happens to be installed."
        ),
        (
            "The second part names no carried file the way the third does, so a record "
            "that has to travel with a spreadsheet or an invoice claims the third part "
            "instead."
        ),
    ),
    labels="Present\nApologies\nNext sitting",
    answers=(
        "Nine members of the council and two of the board\nOne member, on "
        "leave\nThursday 9 April 2026, 17:00"
    ),
)


# The minutes in French.
FRENCH = Words(
    title="Procès-verbal du comité social et économique, 12 mars 2026",
    subject=("Un document conservé trente ans, qui ne contient rien d’autre que "
             "lui-même"),
    heading="Procès-verbal du CSE",
    reference="Jeudi 12 mars 2026, 17 h, salle de réunion 2",
    body=(
        (
            ("Cette feuille déclare suivre la deuxième partie de la norme d’archivage,"
             " au niveau qui ne promet qu’une chose : dans trente ans, la page aura le"
             " même aspect qu’aujourd’hui. Chaque police employée est incorporée au "
             "fichier, chaque couleur dit ce qu’elle signifie, et rien ne dépend d’un "
             "programme, d’un réseau ou une police installée par hasard.")
        ),
        (
            ("La deuxième partie n’admet pas les fichiers joints que prévoit la "
             "troisième : un document qui doit voyager avec un tableur ou une facture "
             "se réclame donc de la troisième.")
        ),
    ),
    labels="Présents\nExcusé\nProchaine séance",
    answers=(
        "Neuf membres du comité et deux de la direction\nUn membre, en congé\nJeudi 9 "
        "avril 2026, 17 h"
    ),
)


# The minutes in German.
GERMAN = Words(
    title="Protokoll des Betriebsrats, 12. März 2026",
    subject=(
        ("Ein Dokument, das dreißig Jahre aufbewahrt wird und nichts als sich selbst "
         "enthält")
    ),
    heading="Protokoll des Betriebsrats",
    reference="Donnerstag, 12. März 2026, 17:00 Uhr, Besprechungsraum 2",
    body=(
        (
            ("Dieses Blatt erklärt Konformität mit dem zweiten Teil der Archivnorm, "
             "auf der Stufe, die genau eines zusagt: Die Seite sieht in dreißig Jahren"
             " so aus wie heute. Jede verwendete Schrift steckt in der Datei, jede "
             "Farbe gibt an, was sie bedeutet, und nichts auf der Seite wartet auf ein"
             " Programm, ein Netzwerk oder eine zufällig installierte Schrift.")
        ),
        (
            ("Der zweite Teil sieht mitgeführte Dateien nicht so vor wie der dritte; "
             "ein Dokument, das mit einer Tabelle oder einer Rechnung reisen muss, "
             "erklärt deshalb Konformität mit dem dritten Teil.")
        ),
    ),
    labels="Anwesend\nEntschuldigt\nNächste Sitzung",
    answers=(
        (("Neun Ratsmitglieder und zwei aus der Leitung\nEin Mitglied, im "
          "Urlaub\nDonnerstag, 9. April 2026, 17:00 Uhr"))
    ),
)


# The minutes in Spanish.
SPANISH = Words(
    title="Acta del comité de empresa, 12 de marzo de 2026",
    subject=(
        "Un documento que se conserva treinta años, sin nada adjunto"
    ),
    heading="Acta del comité de empresa",
    reference="Jueves 12 de marzo de 2026, 17:00, sala de reuniones 2",
    body=(
        (
            ("Esta hoja declara cumplir la segunda parte de la norma de archivo, en el"
             " nivel que promete una sola cosa: dentro de treinta años la página se "
             "verá igual que hoy. Cada fuente que usa va dentro del fichero, cada "
             "color dice lo que significa y nada depende de un programa, de una red ni"
             " de una fuente instalada en el equipo.")
        ),
        (
            ("La segunda parte no admite adjuntar ficheros de cualquier tipo, como sí "
             "hace la tercera; por eso un documento que deba viajar con una hoja de "
             "cálculo o una factura declara la tercera.")
        ),
    ),
    labels="Presentes\nExcusados\nPróxima sesión",
    answers=(
        "Nueve miembros del comité y dos de la dirección\nUn miembro, de "
        "permiso\nJueves 9 de abril de 2026, 17:00"
    ),
)


# The minutes in Italian.
ITALIAN = Words(
    title="Verbale del comitato aziendale, 12 marzo 2026",
    subject="Un documento conservato trent’anni, che non contiene altro che sé stesso",
    heading="Verbale del comitato aziendale",
    reference="Giovedì 12 marzo 2026, ore 17:00, sala riunioni 2",
    body=(
        (
            ("Questo foglio dichiara di rispettare la seconda parte della norma di "
             "archiviazione, al livello che promette una cosa sola: tra trent’anni la "
             "pagina avrà lo stesso aspetto di oggi. Ogni carattere usato è contenuto "
             "nel file, ogni colore dice che cosa significa e nulla sulla pagina "
             "dipende da un programma, da una rete o da un font che si trovi "
             "installato.")
        ),
        (
            ("La seconda parte non prevede, come la terza, allegati di qualsiasi tipo:"
             " un documento che deve viaggiare con un foglio di calcolo o una fattura "
             "dichiara quindi la terza.")
        ),
    ),
    labels="Presenti\nGiustificati\nProssima seduta",
    answers=(
        "Nove membri del comitato e due della direzione\nUn membro, in ferie\nGiovedì "
        "9 aprile 2026, ore 17:00"
    ),
)


# The minutes in Portuguese.
PORTUGUESE = Words(
    title="Ata da comissão de trabalhadores, 12 de março de 2026",
    subject=("Um documento guardado durante trinta anos, que não contém nada além de "
             "si mesmo"),
    heading="Ata da comissão de trabalhadores",
    reference="Quinta-feira, 12 de março de 2026, 17:00, sala de reuniões 2",
    body=(
        (
            "Esta folha declara cumprir a segunda parte da norma de arquivo, no nível "
            "que promete uma única coisa: daqui a trinta anos a página terá o mesmo "
            "aspeto que hoje. Cada tipo de letra está dentro do ficheiro, cada cor diz "
            "o que significa e nada depende de um programa, de uma rede ou de um tipo "
            "de letra instalado por acaso."
        ),
        (
            ("A segunda parte não prevê os ficheiros anexos que a terceira prevê, por "
             "isso um documento que tenha de viajar com uma folha de cálculo ou uma "
             "fatura declara a terceira parte.")
        ),
    ),
    labels="Presentes\nAusências\nPróxima reunião",
    answers=(
        "Nove membros da comissão e dois da direção\nUm membro, de "
        "férias\nQuinta-feira, 9 de abril de 2026, 17:00"
    ),
)


# The minutes in Dutch.
DUTCH = Words(
    title="Notulen van de ondernemingsraad, 12 maart 2026",
    subject="Een document dat dertig jaar bewaard wordt en niets dan zichzelf bevat",
    heading="Notulen van de ondernemingsraad",
    reference="Donderdag 12 maart 2026, 17.00 uur, vergaderzaal 2",
    body=(
        (
            ("Dit blad verklaart te voldoen aan het tweede deel van de archiefnorm, op"
             " het niveau dat maar één ding belooft: over dertig jaar ziet de pagina "
             "er net zo uit als vandaag. Elk gebruikt lettertype zit in het bestand, "
             "elke kleur zegt wat ze betekent, en niets wacht op een programma, een "
             "netwerk of een toevallig geïnstalleerd lettertype.")
        ),
        (
            ("Het tweede deel kent geen meegevoerd bestand zoals het derde; een "
             "document dat met een spreadsheet of factuur moet meereizen, verklaart "
             "daarom te voldoen aan het derde deel.")
        ),
    ),
    labels="Aanwezig\nAfgemeld\nVolgende zitting",
    answers=(
        "Negen leden van de raad en twee van de directie\nEén lid, met "
        "verlof\nDonderdag 9 april 2026, 17.00 uur"
    ),
)

# 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,
}


# The blocks of words the page carries, in the order it draws them: the box each goes
# in, the size it is set at, and the leading and alignment where it states them. A block
# the request states in several runs is one run of words here.
BLOCKS = (
    {
        "text": lambda words: words.heading,
        "size": 20.0,
        "left": 72.0,
        "top": 782.0,
        "width": 451.0,
        "set": None,
    },
    {
        "text": lambda words: words.reference,
        "size": 10.5,
        "left": 72.0,
        "top": 750.0,
        "width": 451.0,
        "set": None,
    },
    {
        "text": lambda words: " ".join(words.body),
        "size": 11.0,
        "left": 72.0,
        "top": 716.0,
        "width": 451.0,
        "set": (16.0, hqf_pdf.Align.Justify),
    },
    {
        "text": lambda words: words.labels,
        "size": 11.0,
        "left": 72.0,
        "top": 596.0,
        "width": 451.0,
        "set": (17.0, hqf_pdf.Align.Left),
    },
    {
        "text": lambda words: words.answers,
        "size": 11.0,
        "left": 200.0,
        "top": 596.0,
        "width": 323.0,
        "set": (17.0, hqf_pdf.Align.Left),
    },
)


def sheet(handle: hqf_pdf.FontHandle, words: Words) -> hqf_pdf.Page:
    """The one page, block by block."""
    content = hqf_pdf.Content()
    for block in BLOCKS:
        settings = {}
        if block["set"] is not None:
            leading, align = block["set"]
            settings = {"leading": leading, "align": align}
        flow = hqf_pdf.TextFlow(handle, block["size"], **settings)
        lines = flow.break_lines(block["text"](words), block["width"])
        # The Python side opens and closes the text object itself, where the Rust side
        # leaves that to the caller.
        flow.draw(content, lines, block["left"], block["top"], block["width"])

    page = hqf_pdf.Page.a4()
    page.set_content(content)
    return page


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("pdfa2b.pdf", language)).stem)

    document = hqf_pdf.Document()
    document.set_license(_licence.licensed())
    document.set_conformance(hqf_pdf.PdfA.A2B)
    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()))
    document.add_page(sheet(handle, words))

    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()
