"""Draws a catalogue whose first column is a picture rather than text.

The Python twin of the `write_picture_cells` example in Rust. The four pictures
are wildly different sizes — one is nine hundred pixels across, another sixteen
— and no two share the same proportions, yet the column they sit in is the width
the table gave it and every row is as tall as its own text. A picture asks its
column for no width and its row for no height: it is fitted, whole and
undistorted, into whatever rectangle it ends up with, and where it sits in that
rectangle is the alignment the cell already had.

The catalogue is held in `Words`, once per language, and `HQF_PDF_LANG` picks which
set is printed. What a picture is called is also what a reader hears in its place: the
alternative text of a picture cell reads the same field as the line beside it, so the
two cannot disagree.

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

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

# The margins, and the box the table is fitted into.
MARGIN = 56.0
TOP = 760.0
BOTTOM = 64.0
PAGE_WIDTH = 595.276
TABLE_WIDTH = PAGE_WIDTH - 2 * MARGIN

# The colour the heading is painted on.
HEAD_FILL = hqf_pdf.Rgb.gray(0.86)

# The height every row of the catalogue takes, whatever its text needs.
ROW_HEIGHT = 54.0

# The pictures the catalogue shows, committed with the tests.
PICTURES = Path(__file__).resolve().parents[2] / "hqf-pdf" / "tests" / "images"


@dataclass(frozen=True)
class Item:
    """One line of the catalogue, save what it is called and what is said about it.

    A picture, a corner and a price are the same in every language; the line reads its
    words from `Words` instead.
    """

    picture: str
    across: hqf_pdf.Align
    down: hqf_pdf.VAlign
    reference: str
    price: str


# The catalogue's lines, in the order they are printed. The pictures are shown against
# each edge of their cells in turn, so that the room a picture does not fill can be seen
# going to one side or the other.
ITEMS = [
    Item(
        "hqf_development.png",
        hqf_pdf.Align.Left,
        hqf_pdf.VAlign.Top,
        "BN-900",
        "148.00",
    ),
    Item(
        "photo.jpg",
        hqf_pdf.Align.Center,
        hqf_pdf.VAlign.Middle,
        "PH-160",
        "24.50",
    ),
    Item(
        "logo.png",
        hqf_pdf.Align.Right,
        hqf_pdf.VAlign.Bottom,
        "SQ-96",
        "9.90",
    ),
    Item(
        "bilevel.png",
        hqf_pdf.Align.Center,
        hqf_pdf.VAlign.Middle,
        "RL-16",
        "3.75",
    ),
]


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

    # The heading of each column, in the order the columns are set.
    headings: tuple[str, str, str, str]
    # What each item is called, in the order the lines are printed. A picture cell
    # carries the same words as the words a reader hears in its place.
    names: tuple[str, str, str, str]
    # What is said about each item.
    notes: tuple[str, str, str, str]


# The catalogue in English.
ENGLISH = Words(
    headings=("Picture", "Item", "Reference", "Price, €"),
    names=("Wide banner", "Photograph", "Square mark", "Two-colour rule"),
    notes=(
        "Nine hundred pixels across and half as tall, drawn against the top "
        "left of its cell.",
        "Four to three, in the middle of its cell — the room it does not fill "
        "is shared either side.",
        "As tall as it is wide, against the bottom right. The height runs out "
        "first, so nothing is left below it.",
        "Sixteen pixels by eight, drawn as large as the cell allows: a picture "
        "is fitted to its cell, not to its own pixels.",
    ),
)

# The catalogue in French.
FRENCH = Words(
    headings=("Image", "Article", "Référence", "Prix, €"),
    names=("Bandeau large", "Photographie", "Marque carrée", "Filet bicolore"),
    notes=(
        "Neuf cents pixels de large et deux fois moins haut, calé en haut "
        "à gauche de sa cellule.",
        "Quatre pour trois, au milieu de sa cellule : la place qu'elle ne "
        "prend pas se partage des deux côtés.",
        "Aussi haute que large, calée en bas à droite. C'est la hauteur qui "
        "manque en premier, donc rien ne reste en dessous.",
        "Seize pixels sur huit, dessiné aussi grand que la cellule le "
        "permet : une image s'ajuste à sa cellule, pas à ses propres pixels.",
    ),
)


# 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 catalogue(
    document: hqf_pdf.Document, font: hqf_pdf.FontHandle, words: Words
) -> hqf_pdf.Table:
    """The catalogue: a heading, then one row per item, the first cell of each a
    picture."""
    # The picture column is a fixed width, the note takes what is left, and the
    # reference and the price are each as wide as their own widest cell.
    columns = hqf_pdf.Columns(
        [
            hqf_pdf.ColumnWidth.points(96.0),
            hqf_pdf.ColumnWidth.fraction(1.0),
            hqf_pdf.ColumnWidth.content(),
            hqf_pdf.ColumnWidth.content(),
        ],
        TABLE_WIDTH,
    )

    table = hqf_pdf.Table(columns)
    table.header(1)
    table.rule(hqf_pdf.Rule.frame(), hqf_pdf.Stroke(0.8))
    hairline = hqf_pdf.Stroke(0.25, hqf_pdf.Rgb.gray(0.75))
    table.rule(hqf_pdf.Rule.horizontal_other(), hairline)
    table.rule(hqf_pdf.Rule.vertical_other(), hairline)
    table.rule(hqf_pdf.Rule.horizontal(1), hqf_pdf.Stroke(0.8))

    pad = hqf_pdf.Padding.symmetric(6.0, 5.0)

    table.push(
        hqf_pdf.Row(
            [
                hqf_pdf.Cell(
                    font, 9.0, words.headings[0], padding=pad, fill=HEAD_FILL
                ),
                hqf_pdf.Cell(
                    font, 9.0, words.headings[1], padding=pad, fill=HEAD_FILL
                ),
                hqf_pdf.Cell(
                    font, 9.0, words.headings[2], padding=pad, fill=HEAD_FILL
                ),
                hqf_pdf.Cell(
                    font,
                    9.0,
                    words.headings[3],
                    align=hqf_pdf.Align.Right,
                    padding=pad,
                    fill=HEAD_FILL,
                ),
            ],
            min_height=20.0,
        )
    )

    for item, name, note in zip(ITEMS, words.names, words.notes):
        image = document.add_image(hqf_pdf.Image.from_path(PICTURES / item.picture))
        table.push(
            hqf_pdf.Row(
                [
                    hqf_pdf.Cell.image(
                        image,
                        alt=name,
                        align=item.across,
                        valign=item.down,
                        padding=pad,
                    ),
                    hqf_pdf.Cell(font, 9.0, f"{name}\n{note}", padding=pad),
                    hqf_pdf.Cell(font, 9.0, item.reference, padding=pad),
                    hqf_pdf.Cell(
                        font,
                        9.0,
                        item.price,
                        align=hqf_pdf.Align.Right,
                        align_on=".",
                        padding=pad,
                    ),
                ],
                min_height=ROW_HEIGHT,
            )
        )

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

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

    table = catalogue(document, font, words)
    placed = table.fit(MARGIN, TOP, TOP - BOTTOM, 0)

    content = hqf_pdf.Content()
    placed.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, {table.row_count} rows")


if __name__ == "__main__":
    main()
