from ekfsm.devices.generic import Device
from ekfsm.devices.io4edge import IO4Edge
from ekfsm.devices.utils import retry
from ekfsm.log import ekfsm_logger
from io4edge_client.pixelDisplay import Client

logger = ekfsm_logger(__name__)


class PixelDisplay(Device):
    """
    Device class for handling a pixel display.
    """

    def __init__(
        self,
        name: str,
        parent: IO4Edge,
        children: list[Device] | None = None,
        abort: bool = False,
        service_suffix: str | None = None,
        *args,
        **kwargs,
    ):
        logger.debug(
            f"Initializing PixelDisplay '{name}' with parent device {parent.deviceId}"
        )

        super().__init__(name, parent, children, abort, *args, **kwargs)

        self.name = name

        if service_suffix is not None:
            self.service_suffix = service_suffix
            logger.debug(f"Using custom service suffix: {service_suffix}")
        else:
            self.service_suffix = name
            logger.debug(f"Using default service suffix: {name}")

        self.service_addr = f"{parent.deviceId}-{self.service_suffix}"
        logger.info(
            f"PixelDisplay '{name}' configured with service address: {self.service_addr}"
        )

        try:
            self.client = Client(self.service_addr, connect=False)
            logger.debug(
                f"PixelDisplay client created for service: {self.service_addr}"
            )
        except Exception as e:
            logger.error(
                f"Failed to create PixelDisplay client for {self.service_addr}: {e}"
            )
            raise

    @retry()
    def describe(self) -> dict:
        """
        Returns a description of the pixel display.
        """
        logger.debug(f"Getting PixelDisplay description for '{self.name}'")
        try:
            describe = self.client.describe()
            desc = {
                "height": describe.height_pixel,
                "width": describe.width_pixel,
                "max_num_of_pixel": describe.max_num_of_pixel,
            }
            logger.debug(f"PixelDisplay '{self.name}' description: {desc}")
            return desc
        except Exception as e:
            logger.error(
                f"Failed to get PixelDisplay description for '{self.name}': {e}"
            )
            raise

    @property
    def height(self) -> int:
        """
        Returns the height of the pixel display in pixels.
        """
        return self.describe()["height"]

    @property
    def width(self) -> int:
        """
        Returns the width of the pixel display in pixels.
        """
        return self.describe()["width"]

    @retry()
    def off(self) -> None:
        """
        Turn off the pixel display.

        Raises
        ------
        RuntimeError
            if the command fails
        TimeoutError
            if the command times out
        """
        logger.info(f"Turning off PixelDisplay '{self.name}'")
        try:
            self.client.set_display_off()
            logger.debug(f"PixelDisplay '{self.name}' successfully turned off")
        except Exception as e:
            logger.error(f"Failed to turn off PixelDisplay '{self.name}': {e}")
            raise

    @retry()
    def display_image(self, pixel: list[list[tuple[int, int, int]]]) -> None:
        """
        Display an image on the pixel display.

        Parameters
        ----------
        pixel : list[list[tuple[int, int, int]]]
            Pixel data to be displayed on the pixel display.

        Raises
        ------
        RuntimeError
            if the command fails
        TimeoutError
            if the command times out
        """
        logger.info(f"Displaying image on PixelDisplay '{self.name}'")
        try:
            with self.client as client:
                logger.debug(f"Sending pixel data to PixelDisplay '{self.name}'")
                for i in range(0, 320, 16):
                    pix_area = []
                    for k in range(0, 16):
                        for j in range(0, 240):
                            pix_area.append(pixel[j][i + k])
                    client.set_pixel_area(0, i, 239, pix_area)
                logger.debug(
                    f"Image successfully displayed on PixelDisplay '{self.name}'"
                )
        except Exception as e:
            logger.error(f"Failed to display image on PixelDisplay '{self.name}': {e}")
            raise

    def __repr__(self):
        return f"{self.name}; Service Address: {self.service_addr}"
