"""Writes a document with a small form: labelled boxes a reader fills in, a
check box a reader ticks, a drop-down a reader picks from, a set of radio
buttons a reader chooses one of, and a field a reader signs.

The Python twin of the `write_form` example in Rust: the same document, through
the binding rather than through the library directly.

Each text field is a labelled rectangle the reader types into. The label is
drawn on the page, as any other text; the box beside it is the field, empty but
for any value it starts with. The check box under them is ticked on or off, the
drop-down under that offers a set of options, the radio buttons under that let
one of several be chosen, and the signature field under those is a place the
reader signs. Reading software draws what is typed, the tick, the chosen
option, and the dot itself. The notes box at the foot of the page is shown on the
screen and left off the paper.

The email field reports its answer under `customer_email` rather than under its
own name, which is the name the program receiving the form reads.

Every field carries a border, so the boxes a reader clicks are plain to see. The
standard draws none by default.

The labels, the options and the document's title are held in `Words`, once per
language, and `HQF_PDF_LANG` picks which set is drawn. A field's name is not among
them: it is what a filled form's data comes back under, so it stays the same
whichever language the page is drawn in.

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

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

# The text fields the form asks for: the name a filled form's data comes back under, and
# the value the field starts with.
FIELDS = [("Name", ""), ("Email", ""), ("Country", "France")]

# The names the two radio buttons send back, in the order they are drawn.
PAYMENT_VALUES = ("card", "transfer")

# The text field whose answer a submitted form reports under another name, and that
# name.
SENT_AS = ("Email", "customer_email")


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

    A field's name is not here: it is the name a filled form's data comes back under,
    and reading software that changed language would send back something else.
    """

    # The line at the head of the page.
    title: str
    # The label drawn beside each text field, in the order `FIELDS` names them.
    labels: tuple[str, str, str]
    # The line beside the check box.
    subscribe: str
    # The label beside the drop-down.
    plan: str
    # The options the drop-down offers; the second is the one it starts on.
    plans: tuple[str, str]
    # The label beside the radio buttons.
    payment: str
    # The line beside each radio button, in the order they are drawn.
    payments: tuple[str, str]
    # The label beside the signature field.
    signature: str
    # The label above the notes box, which is shown on the screen and left off the
    # paper.
    notes: str
    # The title the document carries in its information dictionary.
    document_title: str


# The page in English.
ENGLISH = Words(
    title="Please fill in",
    labels=("Name", "Email", "Country"),
    subscribe="Subscribe to the newsletter",
    plan="Plan",
    plans=("Monthly", "Yearly"),
    payment="Payment",
    payments=("Card", "Transfer"),
    signature="Signature",
    notes="Notes, shown on the screen and left off the paper",
    document_title="A document with a form",
)

# The page in French.
FRENCH = Words(
    title="Merci de remplir ce formulaire",
    labels=("Nom", "Courriel", "Pays"),
    subscribe="S'abonner à la lettre d'information",
    plan="Formule",
    plans=("Mensuelle", "Annuelle"),
    payment="Paiement",
    payments=("Carte", "Virement"),
    signature="Signature",
    notes="Notes, affichées à l'écran et absentes du papier",
    document_title="Un document avec un formulaire",
)


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

# The size the labels and the field text are set at.
SIZE = 12.0

# The colour every field's border is stroked in.
BORDER = hqf_pdf.Rgb.gray(0.6)
# The width every field's border is stroked at, in points.
BORDER_WIDTH = 0.75


def form_page(handle: hqf_pdf.FontHandle, words: Words) -> hqf_pdf.Page:
    """The form page: a title, then a label and a fillable box for each field."""
    content = hqf_pdf.Content()
    page = hqf_pdf.Page.a4()

    content.draw_text(handle, 20.0, 72.0, 760.0, words.title)

    top = 700.0
    for (name, value), label in zip(FIELDS, words.labels):
        content.draw_text(handle, SIZE, 72.0, top, label)
        page.add_field(
            hqf_pdf.TextField(
                name,
                180.0,
                top - 3.0,
                240.0,
                SIZE + 4.0,
                handle,
                SIZE,
                value=value or None,
                border_color=BORDER,
                border_width=BORDER_WIDTH,
                mapping_name=SENT_AS[1] if name == SENT_AS[0] else None,
            )
        )
        top -= SIZE * 3.0

    content.draw_text(handle, SIZE, 90.0, top, words.subscribe)
    page.add_checkbox(hqf_pdf.CheckBox(
            "Subscribe",
            72.0,
            top - 2.0,
            SIZE,
            SIZE,
            checked=True,
            border_color=BORDER,
            border_width=BORDER_WIDTH,
        ))
    top -= SIZE * 3.0

    content.draw_text(handle, SIZE, 72.0, top, words.plan)
    page.add_choice(
        hqf_pdf.ChoiceField(
            "Plan",
            180.0,
            top - 3.0,
            240.0,
            SIZE + 4.0,
            handle,
            SIZE,
            options=list(words.plans),
            selected=words.plans[1],
            combo=True,
            border_color=BORDER,
            border_width=BORDER_WIDTH,
        )
    )
    top -= SIZE * 3.0

    content.draw_text(handle, SIZE, 72.0, top, words.payment)
    page.add_radio(
        hqf_pdf.RadioGroup(
            "Payment",
            [
                (PAYMENT_VALUES[0], 180.0, top - 3.0, SIZE, SIZE),
                (PAYMENT_VALUES[1], 320.0, top - 3.0, SIZE, SIZE),
            ],
            selected=PAYMENT_VALUES[0],
            border_color=BORDER,
            border_width=BORDER_WIDTH,
        )
    )
    content.draw_text(handle, SIZE, 198.0, top, words.payments[0])
    content.draw_text(handle, SIZE, 338.0, top, words.payments[1])
    top -= SIZE * 4.0

    content.draw_text(handle, SIZE, 72.0, top, words.signature)
    page.add_signature(
        hqf_pdf.SignatureField(
            "Signature",
            180.0,
            top - 12.0,
            240.0,
            40.0,
            border_color=BORDER,
            border_width=BORDER_WIDTH,
        )
    )
    top -= SIZE * 5.0

    # Neither printed nor hidden: on the screen, and off the paper.
    content.draw_text(handle, SIZE, 72.0, top, words.notes)
    page.add_field(
        hqf_pdf.TextField(
            "Notes",
            72.0,
            top - 42.0,
            348.0,
            SIZE + 20.0,
            handle,
            SIZE,
            multiline=True,
            border_color=BORDER,
            border_width=BORDER_WIDTH,
            flags=hqf_pdf.AnnotationFlags.none(),
        )
    )

    page.set_content(content)
    return page


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

    document = hqf_pdf.Document()
    document.set_license(_licence.licensed())
    document.set_info("Title", words.document_title)

    handle = document.add_font(hqf_pdf.Font.from_path(_out.font_path()))
    document.add_page(form_page(handle, words))

    written = document.write(out)
    print(f"wrote {out} ({written} bytes)")


if __name__ == "__main__":
    main()
