"""Fills six panels with patterns, each a cell drawn once and repeated.

The Python twin of the `write_tiling` example in Rust. A tiling pattern is a
small drawing and a step. The file holds the cell's operators once, whatever the
area filled with it, and the cell leaves alone everything it does not paint —
which is what lets two patterns cross. The plane is tiled in the page's own
space, so the panels below share one grid rather than each starting its own.

The page is held in `Words`, once per language, and `HQF_PDF_LANG` picks which set is
drawn. What the panels are filled with is not language: the cells, the steps and the
matrix are the same drawing whoever reads the page.

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

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

# The size of one panel, in points.
PANEL = (215.0, 150.0)

# The bottom-left corner of each panel.
CORNERS = [
    (70.0, 560.0),
    (305.0, 560.0),
    (70.0, 380.0),
    (305.0, 380.0),
    (70.0, 200.0),
    (305.0, 200.0),
]

# The name set in pattern-filled type at the foot of the page. A name is not language.
PRODUCT = "hqf-pdf"


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

    What each panel is filled with is not here: a cell, a step and a matrix are the
    same drawing in every language.
    """

    # The line at the head of the page.
    title: str
    # The two lines under it, saying what a cell is and where it is tiled.
    intro: tuple[str, str]
    # What each panel shows, in the order the panels are drawn.
    captions: tuple[str, str, str, str, str, str]
    # The line under the pattern-filled name.
    filled: str


# The page in English.
ENGLISH = Words(
    title="One cell, tiled across whatever it fills",
    intro=(
        "Each panel holds one cell's operators, however wide the panel. The plane "
        "is tiled in the",
        "page's own space, so every panel below is a window onto the same grid.",
    ),
    captions=(
        "Bars, stepping their own width",
        "One dot, stepping wider than itself",
        "The same bars, slanted by the pattern's matrix",
        "Bricks: what leaves the cell's box is cut off",
        "Two patterns, one over the other",
        "The same bars again, at three times the step",
    ),
    filled=(
        "The brick pattern again, this time as the colour a run of text is filled with."
    ),
)

# The page in French.
FRENCH = Words(
    title="Une cellule, répétée sur tout ce qu'elle remplit",
    intro=(
        "Un panneau ne porte qu'une fois les instructions de sa cellule, quelle "
        "que soit sa largeur.",
        "Le plan est pavé dans l'espace de la page : tous les panneaux donnent "
        "sur la même grille.",
    ),
    captions=(
        "Des barres, au pas de leur propre largeur",
        "Un point, au pas plus large que lui",
        "Les mêmes barres, penchées par la matrice",
        "Briques : ce qui sort du cadre est coupé",
        "Deux motifs, l'un par-dessus l'autre",
        "Les mêmes barres, au triple du pas",
    ),
    filled=(
        "Le motif de briques encore, cette fois comme l'encre qui remplit un texte."
    ),
)


# 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 bars(bar: float, step: float, color: hqf_pdf.Rgb) -> hqf_pdf.TilingPattern:
    """A cell holding one bar `bar` points wide, repeated every `step` across."""
    cell = hqf_pdf.Content()
    cell.set_fill(color)
    cell.rect(0.0, 0.0, bar, 8.0)
    cell.fill()
    return hqf_pdf.TilingPattern(
        cell, hqf_pdf.Rect(0.0, 0.0, 8.0, 8.0), (step, 8.0)
    )


def dots(color: hqf_pdf.Rgb) -> hqf_pdf.TilingPattern:
    """A cell holding one square, repeated on a grid three times as wide."""
    cell = hqf_pdf.Content()
    cell.set_fill(color)
    cell.rect(0.0, 0.0, 3.0, 3.0)
    cell.fill()
    return hqf_pdf.TilingPattern(
        cell, hqf_pdf.Rect(0.0, 0.0, 12.0, 12.0), (12.0, 12.0)
    )


def rules(color: hqf_pdf.Rgb) -> hqf_pdf.TilingPattern:
    """A cell holding one bar lying flat, repeated every eight points up."""
    cell = hqf_pdf.Content()
    cell.set_fill(color)
    cell.rect(0.0, 0.0, 8.0, 2.0)
    cell.fill()
    return hqf_pdf.TilingPattern(
        cell, hqf_pdf.Rect(0.0, 0.0, 8.0, 8.0), (8.0, 8.0)
    )


def bricks() -> hqf_pdf.TilingPattern:
    """Two courses of brick, the upper one offset by half a brick.

    The bricks that run off the side of the cell are drawn all the same: the box
    cuts them, and what the next cell along draws is their other half.
    """
    mortar = hqf_pdf.Rgb(0.88, 0.86, 0.83)
    brick = hqf_pdf.Rgb(0.70, 0.35, 0.26)

    cell = hqf_pdf.Content()
    cell.set_fill(mortar)
    cell.rect(0.0, 0.0, 24.0, 24.0)
    cell.fill()
    cell.set_fill(brick)
    for x, y in [
        (0.5, 0.5),
        (12.5, 0.5),
        (-5.5, 12.5),
        (6.5, 12.5),
        (18.5, 12.5),
    ]:
        cell.rect(x, y, 11.0, 11.0)
        cell.fill()

    return hqf_pdf.TilingPattern(
        cell, hqf_pdf.Rect(0.0, 0.0, 24.0, 24.0), (24.0, 24.0)
    )


def panel(
    content: hqf_pdf.Content,
    corner: tuple[float, float],
    fills: list[hqf_pdf.PatternHandle],
) -> None:
    """Fills one panel with each of `fills` in turn, and frames it."""
    width, height = PANEL
    x, y = corner

    content.save_state()
    content.set_fill(hqf_pdf.Rgb(0.98, 0.98, 0.98))
    content.rect(x, y, width, height)
    content.fill()
    for fill in fills:
        content.set_fill_pattern(fill)
        content.rect(x, y, width, height)
        content.fill()
    content.restore_state()

    content.save_state()
    content.set_stroke(hqf_pdf.Rgb.gray(0.55))
    content.set_line_width(0.6)
    content.rect(x, y, width, height)
    content.stroke()
    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("tiling.pdf", language)).stem)

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

    ink = hqf_pdf.Rgb(0.12, 0.28, 0.52)
    upright = document.add_tiling_pattern(bars(3.0, 8.0, ink))
    scattered = document.add_tiling_pattern(dots(hqf_pdf.Rgb(0.16, 0.45, 0.35)))
    slanted = document.add_tiling_pattern(
        bars(3.0, 8.0, ink).matrix((1.0, 0.0, 0.5, 1.0, 0.0, 0.0))
    )
    wall = document.add_tiling_pattern(bricks())
    flat = document.add_tiling_pattern(rules(hqf_pdf.Rgb(0.78, 0.30, 0.24)))
    sparse = document.add_tiling_pattern(bars(3.0, 24.0, ink))

    content = hqf_pdf.Content()
    content.draw_text(font, 15.0, 70.0, 780.0, words.title)
    content.save_state()
    content.set_fill(hqf_pdf.Rgb.gray(0.35))
    content.draw_text(font, 10.0, 70.0, 762.0, words.intro[0])
    content.draw_text(font, 10.0, 70.0, 748.0, words.intro[1])
    content.restore_state()

    fills = [
        [upright],
        [scattered],
        [slanted],
        [wall],
        [upright, flat],
        [sparse],
    ]
    for corner, caption, fill in zip(CORNERS, words.captions, fills):
        panel(content, corner, fill)
        content.save_state()
        content.set_fill(hqf_pdf.Rgb.gray(0.35))
        content.draw_text(font, 9.0, corner[0], corner[1] - 14.0, caption)
        content.restore_state()

    # Text filled with a pattern: the letters are windows onto the tiled plane, not a
    # hundred little copies of the cell.
    content.save_state()
    content.set_fill_pattern(wall)
    content.draw_text(font, 58.0, 70.0, 100.0, PRODUCT)
    content.restore_state()

    content.save_state()
    content.set_fill(hqf_pdf.Rgb.gray(0.35))
    content.draw_text(font, 10.0, 70.0, 80.0, words.filled)
    content.restore_state()

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

    written = document.write(out)
    print(f"wrote {out}: {written} bytes, {len(CORNERS)} panels from 6 cells")


if __name__ == "__main__":
    main()
