"""Paints swatches in each of the three colour spaces, captioned with the operators
they wrote.

The Python twin of the `write_color_spaces` example in Rust. A colour is written in
the space it was named in: a grey as one number, a screen colour as three, a printing
colour as four inks. The bottom row shows one mid-grey said three ways — the same ink
on paper, three different sets of numbers in the file.

The title and the four headings are held in `Words`, once per language, and
`HQF_PDF_LANG` picks which set is drawn. The caption under each swatch is not: it is
the operator the swatch wrote, which reads the same everywhere.

Usage: python examples/write_color_spaces.py [out.pdf]
       HQF_PDF_LANG=fr python examples/write_color_spaces.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
# One swatch, and the gap to the next.
SWATCH = 92.0
GAP = 12.0
# The room a row of swatches takes, caption included.
ROW = 150.0
# The top of the first row of swatches.
FIRST_ROW = 700.0


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

    The caption under a swatch is not here: it is the operator the swatch wrote, and a
    file says ``0.85 0.16 0.16 rg`` wherever it is read.
    """

    # The line at the head of the page.
    title: str
    # What each row of swatches demonstrates, from the top of the page down.
    headings: tuple[str, str, str, str]


# The page in English.
ENGLISH = Words(
    title="Three colour spaces, one page",
    headings=(
        "A grey, said as one number",
        "A screen colour, said as red, green and blue",
        "A printing colour, said as four inks",
        "One mid-grey, said three ways",
    ),
)

# The page in French.
FRENCH = Words(
    title="Trois espaces de couleur, une seule page",
    headings=(
        "Un gris, dit en un seul nombre",
        "Une couleur d'écran, dite en rouge, vert et bleu",
        "Une couleur d'impression, dite en quatre encres",
        "Un même gris moyen, dit de trois façons",
    ),
)


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


@dataclass(frozen=True)
class Band:
    """One row of swatches: what it demonstrates, and the colours it paints."""

    heading: str
    swatches: list[tuple[object, str]]


def bands(words: Words) -> list[Band]:
    """The four rows, from the top of the page down, each under the heading `words`
    gives it."""
    return [
        Band(
            words.headings[0],
            [
                (hqf_pdf.Color.gray(0.15), "0.15 g"),
                (hqf_pdf.Color.gray(0.4), "0.4 g"),
                (hqf_pdf.Color.gray(0.65), "0.65 g"),
                (hqf_pdf.Color.gray(0.9), "0.9 g"),
                (hqf_pdf.Color.gray(1.0), "1 g"),
            ],
        ),
        Band(
            words.headings[1],
            [
                (hqf_pdf.Rgb(0.85, 0.16, 0.16), "0.85 0.16 0.16 rg"),
                (hqf_pdf.Rgb(0.16, 0.6, 0.24), "0.16 0.6 0.24 rg"),
                (hqf_pdf.Rgb(0.16, 0.35, 0.8), "0.16 0.35 0.8 rg"),
                (hqf_pdf.Rgb(0.95, 0.75, 0.1), "0.95 0.75 0.1 rg"),
                (hqf_pdf.Rgb(0.55, 0.2, 0.7), "0.55 0.2 0.7 rg"),
            ],
        ),
        Band(
            words.headings[2],
            [
                (hqf_pdf.Cmyk(1.0, 0.0, 0.0, 0.0), "1 0 0 0 k"),
                (hqf_pdf.Cmyk(0.0, 1.0, 0.0, 0.0), "0 1 0 0 k"),
                (hqf_pdf.Cmyk(0.0, 0.0, 1.0, 0.0), "0 0 1 0 k"),
                (hqf_pdf.Cmyk(0.0, 0.0, 0.0, 1.0), "0 0 0 1 k"),
                (hqf_pdf.Cmyk(0.65, 0.0, 0.35, 0.1), "0.65 0 0.35 0.1 k"),
            ],
        ),
        Band(
            words.headings[3],
            [
                (hqf_pdf.Color.gray(0.5), "0.5 g"),
                (hqf_pdf.Rgb.gray(0.5), "0.5 0.5 0.5 rg"),
                (hqf_pdf.Cmyk(0.0, 0.0, 0.0, 0.5), "0 0 0 0.5 k"),
            ],
        ),
    ]


def draw_band(
    content: hqf_pdf.Content, font: hqf_pdf.FontHandle, band: Band, top: float
) -> None:
    """Draws one row: its heading, its swatches, and under each the operators the
    swatch wrote."""
    content.save_state()
    content.set_fill(hqf_pdf.Rgb.gray(0.15))
    content.draw_text(font, 11.0, MARGIN, top, band.heading)
    content.restore_state()

    bottom = top - 20.0 - SWATCH
    x = MARGIN
    for color, caption in band.swatches:
        content.save_state()
        content.set_fill(color)
        content.rect(x, bottom, SWATCH, SWATCH)
        content.fill()
        content.restore_state()

        content.save_state()
        content.set_stroke(hqf_pdf.Rgb.gray(0.75))
        content.set_line_width(0.5)
        content.rect(x, bottom, SWATCH, SWATCH)
        content.stroke()
        content.restore_state()

        content.save_state()
        content.set_fill(hqf_pdf.Rgb.gray(0.35))
        content.draw_text(font, 7.5, x, bottom - 12.0, caption)
        content.restore_state()

        x += SWATCH + GAP


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("color_spaces.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, 16.0, MARGIN, 760.0, words.title)

    drawn = bands(words)
    top = FIRST_ROW
    for band in drawn:
        draw_band(content, font, band, top)
        top -= ROW

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

    written = document.write(out)
    swatches = sum(len(band.swatches) for band in drawn)
    print(f"wrote {out}: {written} bytes, {swatches} swatches")


if __name__ == "__main__":
    main()
