import ledshim
import logging

from .pixel import Pixel


class Canvas:
    """
    Abstract representation of a single line of pixels (LEDs) on a shim.

    Each LED is represented by a Pixel instance though all instances may not be unique.
    """

    BLANK_PIXEL = Pixel(0, 0, 0, 0)  # An unlit pixel.

    __invert_display = False

    def __init__(self, size: int) -> None:
        """
        Initialise the virtual canvas.
        :param size: the number of pixels on the shim
        """
        if size < 1 or size > 100:
            raise ValueError("Canvas size outside reasonable range of 1 to 100!")
        self.__canvas = [self.BLANK_PIXEL] * size

    @classmethod
    def is_display_inverted(cls) -> float:
        """
        Return the display orientation toggle.
        :return: True if the display should be inverted, otherwise False.
        """
        return cls.__invert_display

    @classmethod
    def invert_display(cls) -> None:
        """
        Reverse the display shown on the shim. This can be toggled to allow for the orientation of the Pi.
        :return: No meaningful return.
        """
        cls.__invert_display = not cls.__invert_display

    def __validate_index(self, p: int) -> None:
        """
        Validate an index position on the current shim is in range.
        :param p: the position on the shim to validate
        :return: Will raise a ValueError if the index position is not in range
        """
        if p < 0 or p >= len(self.__canvas):
            raise ValueError("Pixel index out of range for Canvas!")

    def get_pixel(self, p: int) -> Pixel:
        """
        Get a pixel.
        :param p: the position to return the pixel for
        :return: the pixel instance at the requested position
        """
        self.__validate_index(p)
        return self.__canvas[p]

    def set_pixel(self, p: int, pixel: Pixel) -> None:
        """
        Set a pixel.
        :param p: the position of the pixel to set
        :param pixel: the pixel to set at the given position
        :return: No meaningful return
        """
        self.__validate_index(p)
        self.__canvas[p] = pixel

    def blank_pixel(self, p: int) -> None:
        """
        Blank a pixel.
        :param p: the position of the pixel to blank
        :return: No meaningful return
        """
        self.__validate_index(p)
        self.set_pixel(p, self.BLANK_PIXEL)

    def is_blank_pixel(self, p: int) -> bool:
        """
        Check if a pixel is blank.
        :param p: the position of the pixel to check
        :return: true if the pixel is a __BLANK_PIXEL otherwise false
        """
        return self.get_pixel(p) is self.BLANK_PIXEL

    def clear_all(self) -> None:
        """
        Clear all the pixels.
        :return: No meaningful return
        """
        self.set_all(self.BLANK_PIXEL)

    def set_all(self, pixel: Pixel) -> None:
        """
        Set all the pixels.
        :param pixel: the pixel instance to set
        :return: No meaningful return
        """
        for i in range(self.get_size()):
            self.set_pixel(i, pixel)

    def get_size(self) -> int:
        """
        Get the current number of pixels.
        :return: The number of pixels represented in this instance.
        """
        return len(self.__canvas)

    def __repr__(self) -> str:
        canvas = ["Canvas("]
        for i in range(self.get_size()):
            canvas.append("[{0:2d}, {1}]".format(i, repr(self.get_pixel(i))))
        canvas.append(")")
        return "\n".join(canvas)

    def render_to_shim(self) -> None:
        """
        Display the effect canvas on the shim.
        :return: No meaningful return
        """
        logging.debug(repr(self.__canvas))
        for i in range(self.get_size()):
            pixel = self.get_pixel(i)
            position = (self.get_size() - 1 - i) if Canvas.is_display_inverted() else i
            ledshim.set_pixel(position, pixel.get_r(), pixel.get_g(), pixel.get_b(), pixel.get_brightness())
        ledshim.show()

import ledshim
import logging

from .pixel import Pixel


class Canvas:
    """
    Abstract representation of a single line of pixels (LEDs) on a shim.

    Each LED is represented by a Pixel instance though all instances may not be unique.
    """

    BLANK_PIXEL = Pixel(0, 0, 0, 0)  # An unlit pixel.

    __invert_display = False

    def __init__(self, size: int) -> None:
        """
        Initialise the virtual canvas.
        :param size: the number of pixels on the shim
        """
        if size < 1 or size > 100:
            raise ValueError("Canvas size outside reasonable range of 1 to 100!")
        self.__canvas = [self.BLANK_PIXEL] * size

    @classmethod
    def is_display_inverted(cls) -> float:
        """
        Return the display orientation toggle.
        :return: True if the display should be inverted, otherwise False.
        """
        return cls.__invert_display

    @classmethod
    def invert_display(cls) -> None:
        """
        Reverse the display shown on the shim. This can be toggled to allow for the orientation of the Pi.
        :return: No meaningful return.
        """
        cls.__invert_display = not cls.__invert_display

    def __validate_index(self, p: int) -> None:
        """
        Validate an index position on the current shim is in range.
        :param p: the position on the shim to validate
        :return: Will raise a ValueError if the index position is not in range
        """
        if p < 0 or p >= len(self.__canvas):
            raise ValueError("Pixel index out of range for Canvas!")

    def get_pixel(self, p: int) -> Pixel:
        """
        Get a pixel.
        :param p: the position to return the pixel for
        :return: the pixel instance at the requested position
        """
        self.__validate_index(p)
        return self.__canvas[p]

    def set_pixel(self, p: int, pixel: Pixel) -> None:
        """
        Set a pixel.
        :param p: the position of the pixel to set
        :param pixel: the pixel to set at the given position
        :return: No meaningful return
        """
        self.__validate_index(p)
        self.__canvas[p] = pixel

    def blank_pixel(self, p: int) -> None:
        """
        Blank a pixel.
        :param p: the position of the pixel to blank
        :return: No meaningful return
        """
        self.__validate_index(p)
        self.set_pixel(p, self.BLANK_PIXEL)

    def is_blank_pixel(self, p: int) -> bool:
        """
        Check if a pixel is blank.
        :param p: the position of the pixel to check
        :return: true if the pixel is a __BLANK_PIXEL otherwise false
        """
        return self.get_pixel(p) is self.BLANK_PIXEL

    def clear_all(self) -> None:
        """
        Clear all the pixels.
        :return: No meaningful return
        """
        self.set_all(self.BLANK_PIXEL)

    def set_all(self, pixel: Pixel) -> None:
        """
        Set all the pixels.
        :param pixel: the pixel instance to set
        :return: No meaningful return
        """
        for i in range(self.get_size()):
            self.set_pixel(i, pixel)

    def get_size(self) -> int:
        """
        Get the current number of pixels.
        :return: The number of pixels represented in this instance.
        """
        return len(self.__canvas)

    def __repr__(self) -> str:
        canvas = ["Canvas("]
        for i in range(self.get_size()):
            canvas.append("[{0:2d}, {1}]".format(i, repr(self.get_pixel(i))))
        canvas.append(")")
        return "\n".join(canvas)

    def render_to_shim(self) -> None:
        """
        Display the effect canvas on the shim.
        :return: No meaningful return
        """
        logging.debug(repr(self.__canvas))
        for i in range(self.get_size()):
            pixel = self.get_pixel(i)
            position = (self.get_size() - 1 - i) if Canvas.is_display_inverted() else i
            ledshim.set_pixel(position, pixel.get_r(), pixel.get_g(), pixel.get_b(), pixel.get_brightness())
        ledshim.show()

Abstract representation of a single line of pixels (LEDs) on a shim.      Each LED is represented by a Pixel instance though all instances may not be unique. # An unlit pixel. Initialise the virtual canvas.         :param size: the number of pixels on the shim Return the display orientation toggle.         :return: True if the display should be inverted, otherwise False. Reverse the display shown on the shim. This can be toggled to allow for the orientation of the Pi.         :return: No meaningful return. Validate an index position on the current shim is in range.         :param p: the position on the shim to validate         :return: Will raise a ValueError if the index position is not in range Get a pixel.         :param p: the position to return the pixel for         :return: the pixel instance at the requested position Set a pixel.         :param p: the position of the pixel to set         :param pixel: the pixel to set at the given position         :return: No meaningful return Blank a pixel.         :param p: the position of the pixel to blank         :return: No meaningful return Check if a pixel is blank.         :param p: the position of the pixel to check         :return: true if the pixel is a __BLANK_PIXEL otherwise false Clear all the pixels.         :return: No meaningful return Set all the pixels.         :param pixel: the pixel instance to set         :return: No meaningful return Get the current number of pixels.         :return: The number of pixels represented in this instance. Display the effect canvas on the shim.         :return: No meaningful return