"""A contents page: every entry led to its page number by a row of dots.

The Python twin of the `write_contents` example in Rust.

The dots are not written into the text. Each entry is a cell that asks for a leader,
and the layout fills whatever room the entry leaves — which is why the dots still meet
the numbers when an entry is edited, translated or set in another font.

What the entries say is written in the language `HQF_PDF_LANG` names. What they are
filed under is not: `1`, `2.3` and the page numbers are the shape of the report, and a
translation that renumbered them would send the reader elsewhere.

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

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

# A4's width, and the margins the contents are laid out between.
PAGE_WIDTH = 595.276
MARGIN = 64.0
TABLE_WIDTH = PAGE_WIDTH - 2.0 * MARGIN

# The company the report is about, which no language renames.
COMPANY = "ACME Ltd"

# How many spaces stand between what an entry is filed under and what it says.
GAP = "  "


@dataclass(frozen=True)
class Entry:
    """One line of the contents: how deep it sits, what it is filed under, and the
    page it sends the reader to.

    What the entry says is not here: it is a word, and it lives with the other words of
    its language.
    """

    depth: int
    # The number the entry is filed under, or None for the appendix, which is headed by
    # a word rather than a number.
    number: str | None
    page: str


# The shape of the report this page opens: what each entry is filed under, and where it
# sends the reader.
ENTRIES = (
    Entry(0, "1", "3"),
    Entry(1, "1.1", "3"),
    Entry(1, "1.2", "5"),
    Entry(0, "2", "8"),
    Entry(1, "2.1", "8"),
    Entry(1, "2.2", "11"),
    Entry(1, "2.3", "14"),
    Entry(0, "3", "17"),
    Entry(1, "3.1", "17"),
    Entry(1, "3.2", "20"),
    Entry(1, "3.3", "23"),
    Entry(0, "4", "27"),
    Entry(1, "4.1", "27"),
    Entry(1, "4.2", "31"),
    Entry(0, None, "34"),
)

# How far a sub-entry is set in from the margin, in points.
INDENT = 18.0


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

    What each entry is filed under is not among them: the numbers and the page they
    send the reader to are the shape of the report, and they are held with the entries
    themselves.
    """

    # What the page is called, both drawn at its head and in what the file says of
    # itself.
    title: str
    # What stands before the name of the company in the line under the title.
    subtitle_before: str
    # What stands after it.
    subtitle_after: str
    # What heads the entry that carries no number.
    appendix: str
    # What each entry says, in the order ENTRIES files them.
    entries: tuple[str, ...]


# The contents in English.
ENGLISH = Words(
    title="Contents",
    subtitle_before="Audit of document production — ",
    subtitle_after=", second quarter",
    appendix="Appendix",
    entries=(
        "Scope of the audit",
        "What was examined",
        "What was left out, and why",
        "How the documents are produced today",
        "The invoicing run",
        "The nightly statement of accounts",
        "Documents produced on demand from the counter",
        "What the archival standard asks for",
        "Fonts that travel with the file",
        "Colour that means the same on every screen",
        "Text a reading machine can follow",
        "What it would cost to change",
        "The work, month by month",
        "What is saved once it is done",
        "Files read for this report",
    ),
)

# The contents in French.
FRENCH = Words(
    title="Sommaire",
    subtitle_before="Audit de la production documentaire — ",
    subtitle_after=", deuxième trimestre",
    appendix="Annexe",
    entries=(
        "Périmètre de l'audit",
        "Ce qui a été examiné",
        "Ce qui a été laissé de côté, et pourquoi",
        "Comment les documents sont produits aujourd'hui",
        "La passe de facturation",
        "Le relevé de comptes de la nuit",
        "Documents produits à la demande au guichet",
        "Ce que la norme d'archivage exige",
        "Les polices qui voyagent avec le fichier",
        "Une couleur qui dit la même chose sur tous les écrans",
        "Un texte qu'une machine de lecture peut suivre",
        "Ce que le changement coûterait",
        "Le travail, mois par mois",
        "Ce qu'on économise une fois que c'est fait",
        "Fichiers lus pour ce rapport",
    ),
)

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

# The width of the column the page numbers stand in, in points.
NUMBER_COLUMN = 28.0

# How much of the row an entry leaves free on its right, in points.
RIGHT_PADDING = 4.0

# The size an entry is set at, top level first.
SIZES = (10.5, 9.5)

# Where the head of the table sits, and how much room it is given below.
TABLE_TOP = 730.0
TABLE_ROOM = 660.0

# The sizes the two lines above the table are set at, in points.
TITLE_SIZE = 20.0
SUBTITLE_SIZE = 8.5


def label(words: Words, entry: Entry, index: int) -> str:
    """What one entry reads on the page: what it is filed under, then what it says."""
    filed = words.appendix if entry.number is None else entry.number
    return f"{filed}{GAP}{words.entries[index]}"


def subtitle(words: Words) -> str:
    """The line under the title, which names the company the report is about."""
    return f"{words.subtitle_before}{COMPANY}{words.subtitle_after}"


def contents(font: hqf_pdf.FontHandle, words: Words) -> hqf_pdf.Table:
    """The contents, as one table: the entry, led by dots to its page number."""
    columns = hqf_pdf.Columns(
        [
            hqf_pdf.ColumnWidth.fraction(1.0),
            hqf_pdf.ColumnWidth.points(NUMBER_COLUMN),
        ],
        TABLE_WIDTH,
    )

    table = hqf_pdf.Table(columns)
    table.rule(hqf_pdf.Rule.horizontal(0), hqf_pdf.Stroke(0.5, hqf_pdf.Rgb.gray(0.7)))

    for index, entry in enumerate(ENTRIES):
        top = entry.depth == 0
        size = SIZES[0] if top else SIZES[1]
        color = hqf_pdf.Rgb(0.0, 0.0, 0.0) if top else hqf_pdf.Rgb.gray(0.25)
        padding = hqf_pdf.Padding(
            left=INDENT * entry.depth,
            right=RIGHT_PADDING,
            top=7.0 if top else 2.0,
            bottom=2.0,
        )

        table.push(
            hqf_pdf.Row(
                [
                    # The dots are drawn by the layout, in the room the entry leaves.
                    hqf_pdf.Cell(
                        font,
                        size,
                        label(words, entry, index),
                        color=color,
                        padding=padding,
                        leader=".",
                    ),
                    hqf_pdf.Cell(
                        font,
                        size,
                        entry.page,
                        color=color,
                        align=hqf_pdf.Align.Right,
                        padding=padding,
                    ),
                ]
            )
        )

    return table


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

    document = hqf_pdf.Document()
    document.set_license(_licence.licensed())
    font = document.add_font(hqf_pdf.Font.from_path(_out.font_path()))

    content = hqf_pdf.Content()

    title = hqf_pdf.TextFlow(font, TITLE_SIZE)
    title.draw(
        content,
        title.break_lines(words.title, TABLE_WIDTH),
        MARGIN,
        790.0,
        TABLE_WIDTH,
    )

    under_title = hqf_pdf.TextFlow(font, SUBTITLE_SIZE, color=hqf_pdf.Rgb.gray(0.4))
    under_title.draw(
        content,
        under_title.break_lines(subtitle(words), TABLE_WIDTH),
        MARGIN,
        762.0,
        TABLE_WIDTH,
    )

    contents(font, words).fit(MARGIN, TABLE_TOP, TABLE_ROOM).draw(content)

    page = hqf_pdf.Page.a4()
    page.set_content(content)
    document.add_page(page)

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


if __name__ == "__main__":
    main()
