"""Control Chromecast for pattern generation.

It includes classes to send color patterns, manage connections, and interact
with the Chromecast's pattern generator app for use in display calibration
workflows.
"""
# 0install: Make sure imported protobuf is from implementation to ensure
# correct version

from __future__ import annotations

import sys
from time import sleep
from typing import TextIO

if not getattr(sys, "frozen", False):
    import os
    import re

    for pth in sys.path:
        if os.path.basename(pth).startswith("protobuf-") and re.match(
            r"sha\d+(?:new)?", os.path.basename(os.path.dirname(pth))
        ):
            if "google" in sys.modules:
                del sys.modules["google"]
            syspath = sys.path[:]
            sys.path[:] = [pth]

            sys.path[:] = syspath
            break

from pychromecast import get_chromecasts
from pychromecast.controllers import BaseController

from DisplayCAL import localization as lang


class ChromeCastPatternGeneratorController(BaseController):
    """Controller for the Chromecast pattern generator app."""

    def __init__(self) -> None:
        super().__init__("urn:x-cast:net.hoech.cast.patterngenerator", "B5C2CBFC")
        self.request_id = 0

    def receive_message(self, message: dict, data: dict) -> bool:
        """Handle incoming messages from the Chromecast device.

        Args:
            message (dict): The message received from the Chromecast.
            data (dict): Additional data associated with the message.

        Returns:
            bool: True if the message was handled, False otherwise.
        """
        return True  # Indicate we handled this message

    def send(
        self,
        rgb: tuple[float, float, float] = (0, 0, 0),
        bgrgb: tuple[float, float, float] = (0, 0, 0),
        offset_x: float = 0.5,
        offset_y: float = 0.5,
        h_scale: float = 1,
        v_scale: float = 1,
    ) -> None:
        """Send a color pattern to the Chromecast device.

        Args:
            rgb (tuple): RGB color values (0-1 range).
            bgrgb (tuple): Background RGB color values (0-1 range).
            offset_x (float): Horizontal offset (0-1 range).
            offset_y (float): Vertical offset (0-1 range).
            h_scale (float): Horizontal scale factor.
            v_scale (float): Vertical scale factor.
        """
        fg = "#{:02X}{:02X}{:02X}".format(*tuple(round(v * 255) for v in rgb))
        bg = "#{:02X}{:02X}{:02X}".format(*tuple(round(v * 255) for v in bgrgb))
        self.request_id += 1
        self.send_message(
            {
                "requestId": self.request_id,
                "foreground": fg,
                "offset": [offset_x, offset_y],
                "scale": [h_scale, v_scale],
                "background": bg,
            }
        )


class ChromeCastPatternGenerator:
    """Class to control a Chromecast device for pattern generation.

    Args:
        name (str): The friendly name of the Chromecast device.
        logfile (file-like object, optional): A file-like object to log messages.
    """

    def __init__(self, name: str, logfile: None | TextIO = None) -> None:
        self._controller = ChromeCastPatternGeneratorController()
        self.name = name
        self.listening = False
        self.logfile = logfile

    def disconnect_client(self) -> None:
        """Disconnect the Chromecast client and clean up resources."""
        self.listening = False
        if hasattr(self, "_cc") and self._cc.app_id:
            self._cc.quit_app()
        if hasattr(self, "conn"):
            del self.conn

    def send(
        self,
        rgb: tuple[float, float, float] = (0, 0, 0),
        bgrgb: tuple[float, float, float] = (0, 0, 0),
        bits: None | int = None,
        use_video_levels: None | bool = None,
        x: float = 0,
        y: float = 0,
        w: float = 1,
        h: float = 1,
    ) -> None:
        """Send a color pattern to the Chromecast device.

        Args:
            rgb (tuple): RGB color values (0-1 range).
            bgrgb (tuple): Background RGB color values (0-1 range).
            bits (int, optional): Not used in this implementation.
            use_video_levels (bool, optional): Not used in this implementation.
            x (float): Horizontal offset (0-1 range).
            y (float): Vertical offset (0-1 range).
            w (float): Width of the pattern (0-1 range).
            h (float): Height of the pattern (0-1 range).
        """
        if w < 1:
            x /= 1.0 - w
        else:
            x = 0
        if h < 1:
            y /= 1.0 - h
        else:
            y = 0
        self._controller.send(rgb, bgrgb, x, y, w * 10, h * 10)

    def wait(self) -> None:
        """Wait for Chromecast to be ready and launch the pattern generator app."""
        self.listening = True
        if self.logfile:
            self.logfile.write(
                lang.getstr("connecting.to", ("Chromecast", " " + self.name)) + "\n"
            )
        if not hasattr(self, "_cc"):
            # Find our ChromeCast
            try:
                self._cc = next(
                    cc
                    for cc in get_chromecasts()
                    if str(cc.device.friendly_name) == self.name
                )
            except StopIteration:
                self.listening = False
                raise
            self._cc.register_handler(self._controller)
        # Wait for ChromeCast device to be ready
        while self.listening:
            self._cc.wait(0.05)
            if self._cc.status:
                break
        if self.listening:
            # Launch pattern generator app
            self._controller.launch()
            # Wait for it
            while (
                self.listening and self._cc.app_id != self._controller.supporting_app_id
            ):
                sleep(0.05)
            self.conn = True
            print(lang.getstr("connection.established"))


if __name__ == "__main__":
    from DisplayCAL import config

    config.initcfg()
    lang.init()

    pg = ChromeCastPatternGenerator("Smörebröd")

    # Find our ChromeCast and connect to it, then launch the pattern generator
    # app on the ChromeCast device
    pg.wait()

    # Display test color (yellow window on blue background)
    pg.send((1, 1, 0), (0, 0, 1), x=0.375, y=0.375, w=0.25, h=0.25)
