"""Display four AprilTags for defining a monitor as a Pupil Capture surface."""

from __future__ import annotations

import argparse
import sys
from pathlib import Path
import tkinter as tk

from monitor_utils import format_monitor_list, get_monitor


DEFAULT_TAG_IDS = (0, 1, 2, 3)
SOURCE_TAG_SIZE = 10


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Show four tag36h11 markers in the corners of a screen."
    )
    parser.add_argument(
        "--tag-ids",
        metavar=("TL", "TR", "BL", "BR"),
        nargs=4,
        type=int,
        default=DEFAULT_TAG_IDS,
        help="marker IDs for top-left, top-right, bottom-left, bottom-right",
    )
    parser.add_argument(
        "--tag-size",
        type=int,
        default=200,
        help="displayed marker size in pixels (default: 200)",
    )
    parser.add_argument(
        "--margin",
        type=int,
        default=0,
        help="distance from each screen edge in pixels (default: 0)",
    )
    parser.add_argument(
        "--background",
        default="#f2f2f2",
        help="window background color (default: #f2f2f2)",
    )
    parser.add_argument(
        "--windowed",
        action="store_true",
        help="open a resizable 1280x720 window instead of fullscreen",
    )
    parser.add_argument(
        "--monitor",
        type=int,
        default=None,
        help="Windows display number to use, e.g. 2 for \\\\.\\DISPLAY2 (default: primary)",
    )
    parser.add_argument(
        "--list-monitors",
        action="store_true",
        help="print detected monitors (id, resolution, position, diagonal) and exit",
    )
    return parser.parse_args()


def enable_windows_dpi_awareness() -> None:
    """Keep Tk coordinates in physical pixels on high-DPI Windows displays."""
    if sys.platform != "win32":
        return
    try:
        import ctypes

        ctypes.windll.shcore.SetProcessDpiAwareness(2)
    except (AttributeError, OSError):
        try:
            ctypes.windll.user32.SetProcessDPIAware()
        except (AttributeError, OSError):
            pass


def validate_args(args: argparse.Namespace) -> None:
    if len(set(args.tag_ids)) != 4:
        raise SystemExit("error: all four tag IDs must be unique")
    if any(tag_id < 0 or tag_id > 586 for tag_id in args.tag_ids):
        raise SystemExit("error: tag36h11 IDs must be between 0 and 586")
    if args.tag_size < SOURCE_TAG_SIZE:
        raise SystemExit(f"error: --tag-size must be at least {SOURCE_TAG_SIZE}")
    if args.margin < 0:
        raise SystemExit("error: --margin cannot be negative")


class SurfaceTagWindow:
    def __init__(self, root: tk.Tk, args: argparse.Namespace) -> None:
        self.root = root
        self.args = args
        self.monitor = get_monitor(getattr(args, "monitor", None))
        self.images: list[tk.PhotoImage] = []
        self.canvas = tk.Canvas(
            root,
            background=args.background,
            highlightthickness=0,
            cursor="none",
        )
        self.canvas.pack(fill="both", expand=True)

        root.title("Pupil Capture screen surface markers")
        root.configure(background=args.background)
        if args.windowed:
            root.geometry(f"1280x720+{self.monitor.x + 40}+{self.monitor.y + 40}")
            root.minsize(640, 480)
        else:
            # Tk's "-fullscreen" attribute picks the monitor nearest the window
            # at the moment it's set, which can race the geometry move above and
            # land back on the primary monitor. Pinning an undecorated, topmost
            # window to the exact target rect is deterministic instead.
            root.geometry(
                f"{self.monitor.width}x{self.monitor.height}+{self.monitor.x}+{self.monitor.y}"
            )
            root.overrideredirect(True)
            root.attributes("-topmost", True)
            root.update_idletasks()
            root.geometry(
                f"{self.monitor.width}x{self.monitor.height}+{self.monitor.x}+{self.monitor.y}"
            )
            root.after(50, root.focus_force)

        root.bind("<Escape>", self.close)
        root.bind("q", self.close)
        root.bind("Q", self.close)
        self.canvas.bind("<Configure>", self.draw)

        tag_dir = Path(__file__).resolve().parent / "tag36h11"
        scale = max(1, args.tag_size // SOURCE_TAG_SIZE)
        self.actual_tag_size = SOURCE_TAG_SIZE * scale
        for tag_id in args.tag_ids:
            path = tag_dir / f"tag36_11_{tag_id:05d}.png"
            if not path.is_file():
                raise SystemExit(f"error: marker image not found: {path}")
            # Tk's integer zoom preserves the tag's hard black/white pixel edges.
            self.images.append(tk.PhotoImage(file=path).zoom(scale, scale))

    def close(self, _event: tk.Event | None = None) -> None:
        self.root.destroy()

    def draw(self, event: tk.Event) -> None:
        width, height = event.width, event.height
        size, margin = self.actual_tag_size, self.args.margin
        if width < 2 * (size + margin) or height < 2 * (size + margin):
            self.root.title("Window too small for surface markers")
        else:
            self.root.title("Pupil Capture screen surface markers — Esc/Q to close")

        centers = (
            (margin + size // 2, margin + size // 2),
            (width - margin - size // 2, margin + size // 2),
            (margin + size // 2, height - margin - size // 2),
            (width - margin - size // 2, height - margin - size // 2),
        )
        self.canvas.delete("marker")
        for image, (x, y) in zip(self.images, centers):
            self.canvas.create_image(x, y, image=image, tags="marker")


def main() -> None:
    args = parse_args()
    if args.list_monitors:
        print(format_monitor_list())
        return
    validate_args(args)
    enable_windows_dpi_awareness()
    root = tk.Tk()
    SurfaceTagWindow(root, args)
    root.mainloop()


if __name__ == "__main__":
    main()
