from __future__ import annotations

from collections import defaultdict
from pathlib import Path

import fitz

from .engine import Assignment

PAGE_WIDTH = 612
FORM_HEIGHT = 396
# Exact affine conversion from OMRChecker's normalized 1560 x 960 marker frame
# to the vector bubble centers in the approved RC5 2-up PDF.
FRAME_SCALE = 0.3457333333333333
FRAME_X_OFFSET = 37.13
FRAME_Y_OFFSET = 38.24


def frame_point(x: float, y: float) -> tuple[float, float]:
    return (FRAME_X_OFFSET + x * FRAME_SCALE, FRAME_Y_OFFSET + y * FRAME_SCALE)


def transform_point(x: float, y: float, bottom: bool) -> tuple[float, float]:
    return (PAGE_WIDTH - x, FORM_HEIGHT * 2 - y) if bottom else (x, y)


def draw_value_bubble(page: fitz.Page, x: float, y: float, bottom: bool) -> None:
    center_x, center_y = frame_point(x + 12, y + 12)
    center_x, center_y = transform_point(center_x, center_y, bottom)
    page.draw_circle((center_x, center_y), 4.1, color=(0, 0, 0), fill=(0, 0, 0))


def insert_rotated_text(
    page: fitz.Page, point: tuple[float, float], text: str, fontsize: float, bottom: bool
) -> None:
    point = transform_point(*point, bottom)
    page.insert_text(point, text, fontsize=fontsize, fontname="helv", color=(0, 0, 0), rotate=180 if bottom else 0)


def overlay_assignment(page: fitz.Page, assignment: Assignment, template: dict, bottom: bool) -> None:
    player_id = assignment.player_id.zfill(3)
    round_value = str(assignment.round).zfill(2)
    insert_rotated_text(page, (105, 76), assignment.player_name, 10, bottom)
    insert_rotated_text(page, (125, 111), f"Table {assignment.table} - {assignment.seat}", 10, bottom)

    for digit, x in zip(round_value, (421, 447)):
        insert_rotated_text(page, (x, 57), digit, 11, bottom)
    for digit, x in zip(player_id, (478, 503, 528)):
        insert_rotated_text(page, (x, 57), digit, 11, bottom)

    fields = template["fieldBlocks"]
    round_keys = ("Round_Tens", "Round_Ones")
    player_keys = ("Player_ID_1", "Player_ID_2", "Player_ID_3")
    for digit, key in zip(round_value, round_keys):
        field = fields[key]
        x, y = field["origin"]
        draw_value_bubble(page, x, y + int(digit) * field["bubblesGap"], bottom)
    for digit, key in zip(player_id, player_keys):
        field = fields[key]
        x, y = field["origin"]
        draw_value_bubble(page, x, y + int(digit) * field["bubblesGap"], bottom)


def generate_print_pdf(
    two_up_pdf: Path,
    template: dict,
    assignments: list[Assignment],
    round_count: int,
    destination: Path,
) -> None:
    source = fitz.open(two_up_pdf)
    if len(source) != 1 or source[0].rect.width != PAGE_WIDTH or source[0].rect.height != FORM_HEIGHT * 2:
        raise ValueError("MJ-RC5 2-up source must be one 612 x 792 point letter page")
    by_player: dict[str, list[Assignment]] = defaultdict(list)
    for assignment in assignments:
        by_player[assignment.player_id].append(assignment)
    output = fitz.open()
    split = (round_count + 1) // 2
    for player_id in sorted(by_player, key=lambda value: (int(value), value)):
        rounds = {a.round: a for a in by_player[player_id]}
        for first_round in range(1, split + 1):
            page = output.new_page(width=PAGE_WIDTH, height=FORM_HEIGHT * 2)
            page.show_pdf_page(page.rect, source, 0)
            overlay_assignment(page, rounds[first_round], template, bottom=False)
            second_round = first_round + split
            if second_round <= round_count:
                overlay_assignment(page, rounds[second_round], template, bottom=True)
    output.save(destination, garbage=4, deflate=True)
    output.close()
    source.close()
