"""Sets text in an embedded font, and writes the result to disk.

The Python twin of the `write_text` example in Rust: the same PDF, the same
words, through the binding rather than through the library directly.

`pdftotext` must be able to pull the words back out of the result. If it cannot,
the map back to characters is wrong and the text is unsearchable and uncopyable
even though the page looks right.

The words are held in `Words`, once per language, and `HQF_PDF_LANG` picks which set is
drawn. The number, the issuer and the three amounts are the same in every language.

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

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

# The invoice's number.
NUMBER = "1964413"

# Who issued it.
ISSUER = "HQF Development, Cabriès"

# What each of the three billed lines comes to, in the order they are set.
AMOUNTS = ("4 250.00 EUR", "850.00 EUR", "5 100.00 EUR")

# How many characters a billed line runs to: its label, a space, the leader dots, a
# space, then its amount. The dots take whatever is left, so the amounts end on the same
# character however long the label before them is.
LINE_CHARS = 57


@dataclass(frozen=True)
class Words:
    """Every word the page draws, in one language.

    What is not language stays out of it: the invoice's number, who issued it and the
    three amounts read the same wherever the page is read.
    """

    # The document's title, which a reader shows over the page.
    title: str
    # What stands before the invoice's number.
    number_label: str
    # The line that gives the day it was issued and the term it falls due in.
    dates: str
    # What each billed line is called, in the order AMOUNTS prices them.
    items: tuple[str, str, str]

    def lines(self) -> list[str]:
        """Return the lines the sample page sets, which pdftotext must find again.

        They are not all spelled out of the ASCII range for the look of it: an accent and
        a dash are two glyphs a wrong ToUnicode map loses first, and a page that only
        ever said "Total" would never catch it.
        """
        lines = [f"{self.number_label} {NUMBER} — {ISSUER}", self.dates]
        for item, amount in zip(self.items, AMOUNTS):
            dots = max(0, LINE_CHARS - len(item) - len(amount) - 2)
            lines.append(f"{item} {'.' * dots} {amount}")
        return lines


# The page in English.
ENGLISH = Words(
    title="hqf-pdf text sample",
    number_label="Invoice No.",
    dates="Issued 14 July 2026 — due within 30 days",
    items=("Rendering engine development", "VAT at 20 %", "Total due"),
)

# The page in French.
FRENCH = Words(
    title="hqf-pdf, échantillon de texte",
    number_label="Facture n°",
    dates="Émise le 14 juillet 2026 — payable sous 30 jours",
    items=("Développement du moteur de rendu", "TVA 20 %", "Total à payer"),
)


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


def main() -> None:
    language = _language.from_environment()
    words = _language.words_of(WORDS, language)
    lines = words.lines()

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

    font = hqf_pdf.Font.from_path(_out.font_path())
    print(
        f"font: {font.postscript_name} "
        f"({font.glyph_count} glyphs, {font.units_per_em} units per em)"
    )

    document = hqf_pdf.Document()
    document.set_license(_licence.licensed())  # no notice, so the text stands alone
    document.set_info("Title", words.title)
    handle = document.add_font(font)

    content = hqf_pdf.Content()
    content.begin_text()
    content.set_font(handle, 14.0)
    content.text_position(60.0, 760.0)
    for line in lines:
        content.show_glyphs(handle.glyphs(line))
        # Move down one line. The step is relative to the start of the previous one.
        content.text_position(0.0, -28.0)
    content.end_text()

    # A rule under the total, positioned from the measured width of the text, to prove
    # the metrics agree with what was drawn.
    width = handle.measure(lines[-1], 14.0)
    content.set_line_width(0.8)
    baseline = 760.0 - 28.0 * 5.0
    content.rect(60.0, baseline, width, 0.0)
    content.stroke()

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

    data = document.to_bytes()
    out.parent.mkdir(parents=True, exist_ok=True)
    out.write_bytes(data)

    print(f"wrote {out}: {len(data)} bytes")
    print(f"the last line measures {width:.1f} pt at 14 pt")


if __name__ == "__main__":
    main()
