"""Stamps a page with what it is: DRAFT, COPY, PAID.

The Python twin of the `write_stamp` example in Rust. A stamp is scaled to the box it
is given rather than set in a size, so the same line stamps a whole page, a table
cell and a form field without a single size being worked out by the caller. It is
turned first and scaled second, so that what stays inside the box is the line as the
reader sees it. The page below draws four of them across boxes of very different
shapes.

The stamp takes whatever colour it is given and nothing else: it is text, and a
caller that wants it under the page's own ink draws it first.

The stamped words, the title and the four captions are held in `Words`, once per
language, and `HQF_PDF_LANG` picks which set is drawn. Each set keeps its last word the
longest, because the last box is the one that shows a long word coming out smaller
rather than cut.

Usage: python examples/write_stamp.py [out.pdf]
       HQF_PDF_LANG=fr python examples/write_stamp.py
"""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

MARGIN = 56.0


@dataclass(frozen=True)
class Stamped:
    """One box to stamp: where it is, how big, and which way the line runs."""

    x: float
    y: float
    width: float
    height: float
    slant: hqf_pdf.Slant
    color: hqf_pdf.Rgb


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

    The stamped words are language: a reader stamps a document in the language they read
    it in. Each set keeps its last word the longest, because the last box is the one
    that shows a long word coming out smaller rather than cut.
    """

    # The line at the head of the page.
    title: str
    # What each box is stamped with, in the order the boxes are laid out.
    stamps: tuple[str, str, str, str]
    # What is written under each box.
    captions: tuple[str, str, str, str]


# The page in English.
ENGLISH = Words(
    title="One stamp, four boxes",
    stamps=("DRAFT", "COPY", "PAID", "CANCELLED"),
    captions=(
        "A wide box: the line runs up its diagonal, which is nearly flat.",
        "A tall box, stamped the other way.",
        "Flat across the middle, and wide enough that the width is what binds.",
        "A longer word in a shorter box: the size follows, nothing is cut.",
    ),
)

# The page in French.
FRENCH = Words(
    title="Un tampon, quatre boîtes",
    stamps=("BROUILLON", "COPIE", "PAYÉ", "ANNULATION"),
    captions=(
        "Une boîte large : le mot monte le long de sa diagonale, presque à plat.",
        "Une boîte haute, tamponnée dans l'autre sens.",
        "À plat au milieu, et assez large pour que ce soit la largeur qui limite.",
        "Un mot plus long dans une boîte plus basse : la taille suit, rien n'est coupé.",
    ),
)


# 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 boxes() -> list[Stamped]:
    """The boxes, from the top of the page down."""
    return [
        Stamped(
            MARGIN,
            606.0,
            483.0,
            150.0,
            hqf_pdf.Slant.Up,
            hqf_pdf.Rgb(0.75, 0.16, 0.16),
        ),
        Stamped(
            MARGIN,
            300.0,
            150.0,
            260.0,
            hqf_pdf.Slant.Down,
            hqf_pdf.Rgb(0.25, 0.35, 0.7),
        ),
        Stamped(
            268.0,
            300.0,
            271.0,
            260.0,
            hqf_pdf.Slant.Flat,
            hqf_pdf.Rgb(0.1, 0.45, 0.25),
        ),
        Stamped(
            MARGIN,
            130.0,
            483.0,
            110.0,
            hqf_pdf.Slant.Up,
            hqf_pdf.Rgb(0.45, 0.45, 0.45),
        ),
    ]


def outline(content: hqf_pdf.Content, stamped: Stamped) -> None:
    """Draws the outline of a box, so that what the stamp was fitted to can be seen."""
    content.save_state()
    content.set_stroke(hqf_pdf.Rgb.gray(0.8))
    content.set_line_width(0.5)
    content.rect(stamped.x, stamped.y, stamped.width, stamped.height)
    content.stroke()
    content.restore_state()


def caption(
    content: hqf_pdf.Content, font: hqf_pdf.FontHandle, stamped: Stamped, text: str
) -> None:
    """Writes the caption under a box."""
    content.save_state()
    content.set_fill(hqf_pdf.Rgb.gray(0.35))
    content.draw_text(font, 8.5, stamped.x, stamped.y - 14.0, text)
    content.restore_state()


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

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

    content = hqf_pdf.Content()
    content.draw_text(font, 14.0, MARGIN, 780.0, words.title)

    stamped_boxes = boxes()
    for stamped, text, caption_text in zip(stamped_boxes, words.stamps, words.captions):
        outline(content, stamped)
        stamp = hqf_pdf.Stamp(font, text, slant=stamped.slant, color=stamped.color)
        stamp.draw(content, stamped.x, stamped.y, stamped.width, stamped.height)
        caption(content, font, stamped, caption_text)

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

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


if __name__ == "__main__":
    main()
