"""Writes a document whose pages are numbered by their labels, not by their position.

The Python twin of the `write_page_labels` example in Rust: the same document,
through the binding rather than through the library directly.

Whether the labels work is not a question the bytes can answer: it is answered by the
page box of reading software, which is the one place they show. So every page here says
what reading software ought to be showing for it — open the file and the two either
agree or they do not.

What each page says is written in the language `HQF_PDF_LANG` names. The labels
themselves are not: `i`, `1` and `A-1` are what reading software shows in its page box,
they come from the numbering style and the prefix the file writes, and a page that
translated them would no longer say what the file asks for.

Six runs are labelled by name: roman numerals in small and in capital letters, the
capitals carrying on the count at four; arabic figures, alone and behind a prefix;
letters, capital and small, the small ones behind a prefix. What each page shows is
worked out from its label. The back cover is labelled by its position, with its prefix
and no number, and names the sheets the plates were put under, read back by their name.

The file opens with the thumbnails shown, each under its label, two pages at a time with
the odd ones on the left.

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

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

# How far in from the left edge of the sheet every line is set, in points.
LEFT = 72.0

# How many sheets the document runs to, which each page says of itself.
SHEETS = 14

# The size each line of a page is set at, in points, in the order they are drawn.
SIZES = [12.0, 9.0, 9.0, 14.0, 20.0]

# Where the baseline of each line sits, in points up from the foot of the sheet, in the
# order they are drawn.
BASELINES = [772.0, 752.0, 738.0, 700.0, 640.0]


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

    What reading software shows in its page box is not among them: `i`, `1` and `A-1`
    come from the numbering style and the prefix the file writes, and the page carries
    them so that the two can be read against each other.
    """

    # What the document is called, in what the file says of itself.
    title: str
    # The three lines at the head of every page, which say what page labels are and
    # where they show.
    head: tuple[str, str, str]
    # What stands before a sheet's number.
    sheet: str
    # What stands between a sheet's number and the count of them.
    of: str
    # What stands before the label reading software ought to be showing.
    shows_as: str
    # The label of the back cover, which is its prefix alone.
    back_cover: str
    # The line the back cover adds, naming the sheets the plates are put under;
    # `{sheets}` stands for them.
    plates_at: str


# The pages in English.
ENGLISH = Words(
    title="A document numbered by its labels",
    head=(
        "Page labels number the pages for the reader.",
        (
            "Roman numerals, arabic figures, letters, a prefix, or the prefix alone on "
            "the back cover."
        ),
        "The label shows in reading software's page box, not the sheet number below.",
    ),
    sheet="Sheet",
    of="of",
    shows_as="Reading software shows this page as:",
    back_cover="Cover",
    plates_at="The plates are sheets {sheets}.",
)

# The pages in French.
FRENCH = Words(
    title="Un document numéroté par ses étiquettes",
    head=(
        "Les étiquettes de page numérotent les pages pour le lecteur.",
        (
            "Chiffres romains, arabes, lettres, un préfixe, ou le préfixe seul en "
            "quatrième de couverture."
        ),
        (
            "C’est l’étiquette qui s’affiche dans la case de page du logiciel de "
            "lecture, pas le numéro ci-dessous."
        ),
    ),
    sheet="Feuille",
    of="sur",
    shows_as="Dans le logiciel de lecture :",
    back_cover="Couverture",
    plates_at="Les planches sont les feuilles {sheets}.",
)

# The pages in Dutch.
DUTCH = Words(
    title="Een document genummerd door zijn labels",
    head=(
        "Paginalabels nummeren de pagina’s voor de lezer.",
        (
            "Romeinse cijfers, Arabische cijfers, letters, een voorvoegsel of alleen "
            "het voorvoegsel achterop."
        ),
        "Het label staat in het paginavak van de leessoftware, niet het bladnummer hieronder.",
    ),
    sheet="Blad",
    of="van",
    shows_as="Leessoftware toont deze pagina als:",
    back_cover="Omslag",
    plates_at="De platen zijn de bladen {sheets}.",
)

# The pages in Portuguese.
PORTUGUESE = Words(
    title="Um documento numerado pelas suas etiquetas",
    head=(
        "As etiquetas de página numeram as páginas para o leitor.",
        (
            "Numeração romana, algarismos árabes, letras, um prefixo, ou só o prefixo "
            "na contracapa."
        ),
        "A etiqueta aparece na caixa de página do programa de leitura, não o número de folha abaixo.",
    ),
    sheet="Folha",
    of="de",
    shows_as="O programa de leitura mostra:",
    back_cover="Capa",
    plates_at="As estampas são as folhas {sheets}.",
)

# The pages in Italian.
ITALIAN = Words(
    title="Un documento numerato dalle sue etichette",
    head=(
        "Le etichette di pagina numerano le pagine per il lettore.",
        (
            "Numeri romani, cifre arabe, lettere, un prefisso, o solo il prefisso "
            "sulla quarta di copertina."
        ),
        "La casella di pagina del programma di lettura mostra l’etichetta, non il numero di foglio qui sotto.",
    ),
    sheet="Foglio",
    of="di",
    shows_as="Il programma di lettura mostra:",
    back_cover="Copertina",
    plates_at="Le tavole sono i fogli {sheets}.",
)

# The pages in Spanish.
SPANISH = Words(
    title="Un documento numerado por sus etiquetas",
    head=(
        "Las etiquetas de página numeran las páginas para el lector.",
        (
            "Números romanos, cifras arábigas, letras, un prefijo, o solo el prefijo "
            "en la contraportada."
        ),
        "La etiqueta sale en la casilla de página del programa de lectura, no el número de hoja de abajo.",
    ),
    sheet="Hoja",
    of="de",
    shows_as="El programa de lectura muestra:",
    back_cover="Portada",
    plates_at="Las láminas son las hojas {sheets}.",
)

# The pages in German.
GERMAN = Words(
    title="Ein durch seine Seitenbeschriftungen nummeriertes Dokument",
    head=(
        "Seitenbeschriftungen nummerieren die Seiten für den Leser.",
        (
            "Römische und arabische Zahlen, Buchstaben, ein Präfix oder nur das "
            "Präfix auf der Rückseite."
        ),
        "Die Beschriftung steht im Seitenfeld der Lesesoftware, nicht die Blattnummer unten.",
    ),
    sheet="Blatt",
    of="von",
    shows_as="Lesesoftware zeigt diese Seite als:",
    back_cover="Umschlag",
    plates_at="Die Tafeln sind die Blätter {sheets}.",
)

# 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 runs the document is made of: the name its pages are put under, the label that
# name is numbered by, and how many pages it holds. What reading software ought to show
# for each page is worked out from the label itself.
RUNS = [
    ("foreword", hqf_pdf.PageLabel(hqf_pdf.LabelStyle.LowerRoman), 2),
    ("body", hqf_pdf.PageLabel(hqf_pdf.LabelStyle.Decimal), 3),
    ("plates", hqf_pdf.PageLabel(hqf_pdf.LabelStyle.UpperRoman, start=4), 2),
    ("appendices", hqf_pdf.PageLabel(hqf_pdf.LabelStyle.UpperLetters), 2),
    ("notes", hqf_pdf.PageLabel(hqf_pdf.LabelStyle.LowerLetters, prefix="n-"), 2),
    ("annex", hqf_pdf.PageLabel(hqf_pdf.LabelStyle.Decimal, prefix="A-"), 2),
]


def lines(words: Words, sheet: int, shown: str) -> list[str]:
    """What one page says: what page labels are, which sheet it is, and its label."""
    return [
        *words.head,
        f"{words.sheet} {sheet} {words.of} {SHEETS}",
        f"{words.shows_as} {shown}",
    ]


def page(
    handle: hqf_pdf.FontHandle,
    words: Words,
    sheet: int,
    shown: str,
    note: str | None = None,
) -> hqf_pdf.Page:
    """A page that says which sheet it is and what reading software ought to call it,
    and the note it may add."""
    content = hqf_pdf.Content()
    for line, size, top in zip(lines(words, sheet, shown), SIZES, BASELINES):
        content.draw_text(handle, size, LEFT, top, line)
    if note is not None:
        content.draw_text(handle, 12.0, LEFT, 600.0, note)

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


def main() -> None:
    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 two
    # languages do not overwrite each other in `tmp/`.
    out = _out.output_path(Path(_language.file_name("page-labels.pdf", language)).stem)

    document = hqf_pdf.Document()
    document.set_license(_licence.licensed())
    document.set_info("Title", words.title)

    handle = document.add_font(hqf_pdf.Font.from_path(_out.font_path()))

    sheet = 1
    for name, label, pages in RUNS:
        first = document.page_count
        for offset in range(pages):
            document.add_page(page(handle, words, sheet, label.shown_at(offset)))
            sheet += 1
        document.name_pages(name, range(first, document.page_count))
        document.label_named_pages(name, label)

    # The back cover, labelled by its prefix alone, which spells no number; it names the
    # sheets the plates were put under, read back by their name.
    plates = document.named_pages("plates") or []
    sheets = ", ".join(str(rank + 1) for rank in plates)
    shown = f"{words.back_cover}{hqf_pdf.LabelStyle.PrefixOnly.spell(1)}"
    note = words.plates_at.replace("{sheets}", sheets)
    document.add_page(page(handle, words, sheet, shown, note))
    document.label_pages(
        document.page_count - 1,
        hqf_pdf.PageLabel(hqf_pdf.LabelStyle.PrefixOnly, prefix=words.back_cover),
    )

    document.set_open_mode(hqf_pdf.OpenMode.Thumbnails)

    document.set_page_layout(hqf_pdf.PageLayout.TwoPagesOddLeft)

    written = document.write(out)
    print(f"wrote {out} ({written} bytes)")


if __name__ == "__main__":
    main()
