"""A chart of colours, and a drawing that names places in it.

The Python twin of the `write_color_table` example in Rust. A colour in such a
space is one number, and that number is not a colour: it is a row of the table
the document carries. The same sixteen numbers are drawn twice below, through
two tables, and what changes is the table.

The page is held in `Words`, once per language, and `HQF_PDF_LANG` picks which set is
drawn. The two tables are not language: eight rows of red, green and blue are the same
eight rows whoever reads the page.

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

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

# The left edge of everything.
LEFT = 70.0

# The house colours, eight entries of red, green and blue.
HOUSE = bytes(
    [
        0x1B, 0x36, 0x5D,  # deep blue
        0x2E, 0x86, 0x86,  # teal
        0xE8, 0xB3, 0x3B,  # gold
        0xD9, 0x5F, 0x43,  # coral
        0x7A, 0x4B, 0x7E,  # plum
        0xE3, 0xD5, 0xB8,  # sand
        0x5C, 0x67, 0x70,  # slate
        0x18, 0x1C, 0x22,  # ink
    ]
)

# The same eight places, filled in for a different season.
SEASON = bytes(
    [
        0x0B, 0x4F, 0x3A,  # pine
        0x8C, 0xC0, 0x63,  # moss
        0xF2, 0xE8, 0xC9,  # linen
        0xB0, 0x2E, 0x4A,  # berry
        0x3D, 0x2B, 0x1F,  # bark
        0xE7, 0xA3, 0x6B,  # clay
        0x9A, 0xA5, 0x9E,  # lichen
        0x14, 0x18, 0x14,  # peat
    ]
)

# The drawing itself: sixteen places in whichever table is being read, laid out four to
# a row from the top down.
PLACES = [
    0.0, 1.0, 2.0, 3.0,
    1.0, 4.0, 5.0, 2.0,
    2.0, 5.0, 6.0, 1.0,
    3.0, 2.0, 1.0, 7.0,
]

# How many places are drawn across.
ACROSS = 4

# The size of one swatch of a chart, the gap to the next, and the height.
CHART_CELL = 50.0
CHART_GAP = 6.0
CHART_HEIGHT = 34.0

# The size of one square of the drawing, and the gap to the next.
SQUARE = 44.0
SQUARE_GAP = 5.0


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

    The two tables are not here: eight rows of red, green and blue are the same eight
    rows in every language, and so are the numbers under the swatches.
    """

    # The line at the head of the page.
    title: str
    # The two lines under it, saying what the drawings below are.
    intro: tuple[str, str]
    # The first panel's heading and the line under it.
    house: tuple[str, str]
    # The second panel's, once the table has been swapped.
    season: tuple[str, str]
    # The two lines at the foot of the page.
    closing: tuple[str, str]


# The page in English.
ENGLISH = Words(
    title="A chart of colours, and a drawing that names places in it",
    intro=(
        "The two drawings below are the same sixteen numbers. Neither of them "
        "says a colour: each says",
        "which row of the table to look in, and the document carries the table.",
    ),
    house=(
        "The house colours",
        "Eight rows of red, green and blue, carried in the file and named 0 to 7.",
    ),
    season=(
        "The same drawing, one table later",
        "The same eight places, filled in for another season. The drawing was "
        "not touched.",
    ),
    closing=(
        "Repainting the house is one table, not one drawing: nothing above was "
        "drawn twice, and nothing",
        "in either drawing had to be found and changed.",
    ),
)

# The page in French.
FRENCH = Words(
    title="Une table de couleurs, et un dessin qui nomme ses lignes",
    intro=(
        "Les deux dessins ci-dessous sont les mêmes seize nombres. Aucun ne dit "
        "une couleur : chacun dit",
        "quelle ligne de la table regarder, et c'est le document qui porte la table.",
    ),
    house=(
        "Les couleurs de la maison",
        "Huit lignes de rouge, de vert et de bleu, portées par le fichier et "
        "nommées 0 à 7.",
    ),
    season=(
        "Le même dessin, une table plus tard",
        "Les mêmes huit lignes, remplies pour une autre saison. Le dessin n'a "
        "pas été touché.",
    ),
    closing=(
        "Repeindre la maison, c'est changer une table, pas un dessin : rien n'a "
        "été dessiné deux fois,",
        "et rien n'a eu à être retrouvé ni modifié dans l'un ou l'autre dessin.",
    ),
)


# 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 heading(content, font, words):
    """Draw the title and the two lines saying what the page is showing."""
    content.draw_text(font, 15.0, LEFT, 780.0, words.title)
    content.save_state()
    content.set_fill(hqf_pdf.Color.gray(0.35))
    content.draw_text(font, 10.0, LEFT, 762.0, words.intro[0])
    content.draw_text(font, 10.0, LEFT, 748.0, words.intro[1])
    content.restore_state()


def panel_heading(content, font, y, title, under):
    """Draw one panel's heading, with the line under it."""
    content.save_state()
    content.set_fill(hqf_pdf.Color.gray(0.2))
    content.draw_text(font, 11.0, LEFT, y, title)
    content.set_fill(hqf_pdf.Color.gray(0.45))
    content.draw_text(font, 8.0, LEFT, y - 15.0, under)
    content.restore_state()


def chart(content, font, space, entries, bottom):
    """Draw the table itself, one swatch per row, under the number naming it."""
    x = LEFT
    place = 0.0
    for _ in range(entries):
        content.save_state()
        content.set_fill_space(space)
        content.set_fill_components([place])
        content.rect(x, bottom, CHART_CELL, CHART_HEIGHT)
        content.fill()
        content.restore_state()

        content.save_state()
        content.set_fill(hqf_pdf.Color.gray(0.45))
        content.draw_text(font, 8.0, x, bottom - 12.0, f"{place:.0f}")
        content.restore_state()

        x += CHART_CELL + CHART_GAP
        place += 1.0


def drawing(content, space, top):
    """Draw the sixteen places, four to a row from the top down."""
    x = LEFT
    y = top - SQUARE
    for count, place in enumerate(PLACES):
        content.save_state()
        content.set_fill_space(space)
        content.set_fill_components([place])
        content.rect(x, y, SQUARE, SQUARE)
        content.fill()
        content.restore_state()

        if (count + 1) % ACROSS == 0:
            x = LEFT
            y -= SQUARE + SQUARE_GAP
        else:
            x += SQUARE + SQUARE_GAP


def closing(content, font, words):
    """Draw the closing note."""
    content.save_state()
    content.set_fill(hqf_pdf.Color.gray(0.35))
    content.draw_text(font, 9.0, LEFT, 80.0, words.closing[0])
    content.draw_text(font, 9.0, LEFT, 66.0, words.closing[1])
    content.restore_state()


def table(entries):
    """The table `entries` hold, read through the profile the library builds."""
    profile = hqf_pdf.IccBased(hqf_pdf.srgb_icc_profile(), hqf_pdf.DeviceSpace.Rgb)
    return hqf_pdf.Indexed(profile, entries)


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

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

    house = table(HOUSE)
    season = table(SEASON)
    entries = house.entries
    house = document.add_color_space(house)
    season = document.add_color_space(season)

    content = hqf_pdf.Content()
    heading(content, font, words)

    panel_heading(content, font, 700.0, words.house[0], words.house[1])
    chart(content, font, house, entries, 640.0)
    drawing(content, house, 612.0)

    panel_heading(content, font, 380.0, words.season[0], words.season[1])
    chart(content, font, season, entries, 320.0)
    drawing(content, season, 292.0)
    closing(content, font, words)

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

    written = document.write(out)
    print(
        f"wrote {out}: {written} bytes, {len(PLACES)} places drawn twice "
        f"through two tables of {entries} rows"
    )


if __name__ == "__main__":
    main()
