"""Draws a specification sheet whose cells are merged down the rows.

The Python twin of the `write_merged_cells` example in Rust. Each group of
properties names itself once, in a cell covering all the rows of the group, and
carries one note beside them in another: the rows beneath hand their cells to
the columns those two leave free, and the hairlines between the rows stop at
their edges. The last group shows what a merged cell does when it needs more
room than its rows give it: every row it covers grows by the same share of what
is missing.

Every word the sheet draws is held in `Words`, once per language, and `HQF_PDF_LANG`
picks which one is drawn. The model's number and the figures are the same in both.

Usage: python examples/write_merged_cells.py [out.pdf] [font.ttf]
       HQF_PDF_LANG=fr python examples/write_merged_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 a group's name and its note are painted on.
GROUP_FILL = hqf_pdf.Rgb(0.90, 0.91, 0.94)


# How many properties each group holds, in the order they are printed.
GROUP_ROWS = (4, 3, 2, 2)


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

    What is not language stays out of it: the model's number, the figures and the units
    they are given in read the same in every language.
    """

    # The heading across the four columns.
    title: str
    # What each group is called, written once beside all of its rows.
    groups: tuple[str, ...]
    # What is said about each group as a whole, once beside all of its rows. The last is
    # far longer than the two rows it covers, which is what makes the sheet's last row
    # grow.
    notes: tuple[str, ...]
    # What each property is called, group after group.
    properties: tuple[str, ...]
    # What each of them is worth, in the same order.
    values: tuple[str, ...]


# The sheet in English.
ENGLISH = Words(
    title="Model HQF-220 — technical data",
    groups=("Body", "Optics", "Mounting", "Power"),
    notes=(
        "Measured without the mounting plate.",
        "Figures hold across the whole of the zoom range.",
        "Plate sold with the instrument.",
        "Twelve volts, from the adapter supplied or from any regulated bench "
        "supply. The instrument draws its full current for the first two seconds "
        "after it is switched on, so a supply rated at the working figure alone "
        "will trip.",
    ),
    properties=(
        "Material",
        "Mass",
        "Finish",
        "Sealing",
        "Focal length",
        "Widest aperture",
        "Coating",
        "Thread",
        "Plate",
        "Supply",
        "Draw",
    ),
    values=(
        "Anodised aluminium",
        "1.84 kg",
        "Matt black, bead blasted",
        "IP65",
        "24 to 70 mm",
        "f/2.8",
        "Multi-layer, both faces",
        "M6, four points",
        "Quick release",
        "12 V DC",
        "9 W at full output",
    ),
)

# The sheet in French.
FRENCH = Words(
    title="Modèle HQF-220 — caractéristiques techniques",
    groups=("Boîtier", "Optique", "Fixation", "Alimentation"),
    notes=(
        "Mesuré sans la platine de fixation.",
        "Valeurs valables sur toute la plage de zoom.",
        "Platine livrée avec l'appareil.",
        "Douze volts, par l'adaptateur fourni ou par toute alimentation de "
        "laboratoire régulée. L'appareil appelle son courant maximal pendant "
        "les deux premières secondes après la mise sous tension : une "
        "alimentation calibrée sur la seule valeur nominale déclenchera.",
    ),
    properties=(
        "Matériau",
        "Masse",
        "Finition",
        "Étanchéité",
        "Focale",
        "Ouverture maximale",
        "Traitement",
        "Filetage",
        "Platine",
        "Source",
        "Consommation",
    ),
    values=(
        "Aluminium anodisé",
        "1.84 kg",
        "Noir mat, microbillé",
        "IP65",
        "24 à 70 mm",
        "f/2.8",
        "Multicouche, deux faces",
        "M6, quatre points",
        "Attache rapide",
        "12 V continu",
        "9 W à pleine puissance",
    ),
)


# 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 sheet(font: hqf_pdf.FontHandle, words: Words) -> hqf_pdf.Table:
    """The specification sheet: a heading across the table, then two merged
    cells per group of properties."""
    # The property column takes whatever the other three leave it.
    columns = hqf_pdf.Columns(
        [
            hqf_pdf.ColumnWidth.points(76.0),
            hqf_pdf.ColumnWidth.fraction(1.0),
            hqf_pdf.ColumnWidth.points(120.0),
            hqf_pdf.ColumnWidth.points(150.0),
        ],
        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)

    # The heading covers the four columns, the way a cell covers four rows further down:
    # one span across, one down.
    table.push(
        hqf_pdf.Row(
            [
                hqf_pdf.Cell(
                    font,
                    11.0,
                    words.title,
                    span=4,
                    align=hqf_pdf.Align.Center,
                    valign=hqf_pdf.VAlign.Middle,
                    padding=pad,
                    fill=hqf_pdf.Rgb.gray(0.86),
                )
            ],
            min_height=24.0,
        )
    )

    first = 0
    for group, rows in enumerate(GROUP_ROWS):
        for index in range(rows):
            prop = words.properties[first + index]
            value = words.values[first + index]
            cells = []
            # The group names itself on its first row only, and so does its note. The
            # rows under them hold two cells, not four: the first column and the last
            # are taken.
            if index == 0:
                cells.append(
                    hqf_pdf.Cell(
                        font,
                        10.0,
                        words.groups[group],
                        span_rows=rows,
                        align=hqf_pdf.Align.Center,
                        valign=hqf_pdf.VAlign.Middle,
                        padding=pad,
                        fill=GROUP_FILL,
                    )
                )
            cells.append(hqf_pdf.Cell(font, 9.0, prop, padding=pad))
            cells.append(hqf_pdf.Cell(font, 9.0, value, padding=pad))
            if index == 0:
                cells.append(
                    hqf_pdf.Cell(
                        font,
                        8.0,
                        words.notes[group],
                        span_rows=rows,
                        align=hqf_pdf.Align.Justify,
                        padding=pad,
                        fill=GROUP_FILL,
                    )
                )
            table.push(hqf_pdf.Row(cells, min_height=18.0))
        first += rows

    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("merged_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 = sheet(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()
