import logging
from acmenra_cv.render.font import Font
from typing import List, Tuple

logger = logging.getLogger(__name__)


class Style:
    """
    Centralized configuration container for visualization and rendering parameters.

    This class manages all stylistic aspects of drawing operations, including color palettes,
    line properties, shape filling, transparency, and text rendering. Designed to provide
    a consistent, type-safe interface for configuring visual elements across computer
    vision pipelines and ADAS monitoring systems.

    Key features:
    - Strict type and range validation for all visualization parameters
    - Unique color palette enforcement with RGB tuple validation
    - Configurable corner rounding and segmentation for bounding boxes
    - Polygon smoothing controls for cleaner mask visualization
    - Alpha compositing support for transparent overlays
    - Global show/hide toggle for resource optimization on embedded devices

    Designed for seamless integration with downstream rendering utilities such as:
    - Drawer class for frame overlay operations
    - Real-time visualization pipelines
    - Multi-camera monitoring systems
    - Performance-optimized embedded deployments (e.g., Raspberry Pi, Jetson)

    Attributes:
        _font (Font): Font configuration for text rendering elements.
        _palette (List[Tuple[int, int, int]]): List of unique RGB color tuples.
        _thickness (int): Line thickness for drawing boundaries.
        _rounding (float): Corner rounding factor for boxes [0.0, 0.5].
        _segment (float): Segment length factor for box corners [0.0, 0.5].
        _smooth (int): Smoothing factor for polygon vertices.
        _alpha (float): Transparency level for filled areas [0.0, 1.0].
        _fill (bool): Whether to fill shapes with color.
        _show (bool): Global toggle for visualization rendering.

    Note:
        - `show=False` completely disables rendering for performance optimization
        - All numeric parameters are validated on assignment
        - Palette colors must be unique RGB tuples within [0, 255] range
    """

    def __init__(self,
                 palette: List[tuple],
                 font: Font,
                 thickness: int = 5,
                 rounding: float = 0.04,
                 segment: float = 0.2,
                 smooth: int = 2,
                 alpha: float = 0.65,
                 fill: bool = False,
                 show: bool = False):
        """
        Initializes a Style object with comprehensive visualization parameters.

        Args:
            palette (List[Tuple[int, int, int]]): List of unique RGB color tuples for object visualization.
            font (Font): Font configuration for text rendering elements.
            thickness (int, optional): Line thickness for drawing boundaries. Defaults to 5.
            rounding (float, optional): Corner rounding factor for boxes [0.0, 0.5]. Defaults to 0.04.
            segment (float, optional): Segment length factor for box corners [0.0, 0.5]. Defaults to 0.2.
            smooth (int, optional): Smoothing factor for polygon rendering. Defaults to 2.
            alpha (float, optional): Transparency level for filled areas [0.0, 1.0]. Defaults to 0.65.
            fill (bool, optional): Whether to fill shapes with color. Defaults to False.
            show (bool, optional): Global toggle for visualization rendering.
                Defaults to False (disabled for resource optimization on embedded devices).

        Raises:
            TypeError: If any parameter has incorrect type.
            ValueError: If numeric parameters are outside valid ranges or palette contains duplicate colors.

        Note:
            - `show=False` bypasses all drawing operations for maximum performance
            - Palette colors must be unique to avoid class ID mapping conflicts
            - All validation is delegated to property setters
        """
        self.font = font
        self.palette = palette
        self.thickness = thickness
        self.rounding = rounding
        self.segment = segment
        self.smooth = smooth
        self.alpha = alpha
        self.fill = fill
        self.show = show

    @property
    def font(self) -> Font:
        """
        Font: Gets the font configuration used for text rendering.

        Returns:
            Font: Font object containing face, scale, thickness, and spacing parameters.
        """
        return self._font

    @font.setter
    def font(self, font: Font) -> None:
        """
        Sets the font configuration for text rendering.

        Args:
            font (Font): Font object with text rendering parameters.

        Raises:
            TypeError: If font is not an instance of Font.
        """
        if not isinstance(font, Font):
            raise TypeError(f"'font' must be '<Font>', but got {type(font).__name__}")
        self._font = font

    @property
    def palette(self) -> List[Tuple[int, int, int]]:
        """
        List[Tuple[int, int, int]]: Gets the color palette for object visualization.

        Returns:
            List[Tuple[int, int, int]]: List of unique RGB tuples, one per object class.
        """
        return self._palette

    @palette.setter
    def palette(self, palette: List[Tuple[int, int, int]]) -> None:
        """
        Sets the color palette with comprehensive validation.

        Args:
            palette (List[Tuple[int, int, int]]): New color palette as RGB tuples.

        Raises:
            TypeError: If palette is not a list or contains invalid color formats.
            ValueError: If RGB values are outside [0, 255] or colors are not unique.

        Note:
            - Uniqueness is enforced to prevent class ID mapping conflicts
            - Each tuple must contain exactly 3 integers
        """
        if not isinstance(palette, List):
            raise TypeError(f"'palette' must be '<List[Tuple[int, int, int]]>', but got {type(palette).__name__}")

        for color in palette:
            if not isinstance(color, List):
                raise TypeError(f"'color' must be '<Tuple[int, int, int]>', but got {type(color).__name__}")

            for value in color:
                if not isinstance(value, int) or isinstance(value, bool):
                    raise TypeError(f"All elements in 'color' must be '<int>', but got {type(value).__name__}")
                if not 0 <= value <= 255:
                    raise ValueError(f"'value' should be in [0, 255], but got '{value}'")

        if len(palette) != len(list(set(set(tuple(color) for color in palette)))):
            raise ValueError(f"'colors' should have unique identifiers, but it turned out to be '{palette}'")
        self._palette = palette

    @property
    def thickness(self) -> int:
        """
        int: Gets the line thickness for drawing boundaries.

        Returns:
            int: Line thickness in pixels (non-negative integer).
        """
        return self._thickness

    @thickness.setter
    def thickness(self, thickness: int) -> None:
        """
        Sets the line thickness with validation.

        Args:
            thickness (int): New line thickness value.

        Raises:
            TypeError: If thickness is not an integer.
            ValueError: If thickness is negative.
        """
        if not isinstance(thickness, int) or isinstance(thickness, bool):
            raise TypeError(f"'thickness' must be '<int>', but got {type(thickness).__name__}")
        if thickness < 0:
            raise ValueError(f"'thickness' must be >= 0")
        self._thickness = thickness

    @property
    def rounding(self) -> float:
        """
        float: Gets the corner rounding factor for bounding boxes.

        Returns:
            float: Rounding factor [0.0, 0.5] (0.0 = square, 0.5 = maximum rounding).
        """
        return self._rounding

    @rounding.setter
    def rounding(self, rounding: float) -> None:
        """
        Sets the corner rounding factor with range validation.

        Args:
            rounding (float): New rounding factor.

        Raises:
            TypeError: If rounding is not a float.
            ValueError: If rounding is outside [0.0, 0.5].
        """
        if not isinstance(rounding, float):
            raise TypeError(f"'rounding' must be '<float>', but got {type(rounding).__name__}")
        if not 0.0 <= rounding <= 0.5:
            raise ValueError(f"'rounding' must be [0, 0.5]")
        self._rounding = rounding

    @property
    def segment(self) -> float:
        """
        float: Gets the segment length factor for box corners.

        Returns:
            float: Segment factor [0.0, 0.5] (0.0 = no straight segments, 0.5 = maximum).
        """
        return self._segment

    @segment.setter
    def segment(self, segment: float) -> None:
        """
        Sets the segment length factor with range validation.

        Args:
            segment (float): New segment factor.

        Raises:
            TypeError: If segment is not a float.
            ValueError: If segment is outside [0.0, 0.5].
        """
        if not isinstance(segment, float):
            raise TypeError(f"'segment' must be '<float>', but got {type(segment).__name__}")
        if not 0.0 <= segment <= 0.5:
            raise ValueError(f"'segment' must be [0, 0.5]")
        self._segment = segment

    @property
    def smooth(self) -> int:
        """
        int: Gets the smoothing factor for polygon vertices.

        Returns:
            int: Smoothing level (non-negative integer).
        """
        return self._smooth

    @smooth.setter
    def smooth(self, smooth: int) -> None:
        """
        Sets the polygon smoothing factor with validation.

        Args:
            smooth (int): New smoothing level.

        Raises:
            TypeError: If smooth is not an integer.
            ValueError: If smooth is negative.
        """
        if not isinstance(smooth, int) or isinstance(smooth, bool):
            raise TypeError(f"'smooth' must be '<int>', but got {type(smooth).__name__}")
        if smooth < 0:
            raise ValueError(f"'smooth' must be >= 0")
        self._smooth = smooth

    @property
    def alpha(self) -> float:
        """
        float: Gets the transparency level for filled areas.

        Returns:
            float: Alpha value [0.0, 1.0] (0.0 = transparent, 1.0 = opaque).
        """
        return self._alpha

    @alpha.setter
    def alpha(self, alpha: float) -> None:
        """
        Sets the transparency level with range validation.

        Args:
            alpha (float): New transparency value.

        Raises:
            TypeError: If alpha is not a float.
            ValueError: If alpha is outside [0.0, 1.0].
        """
        if not isinstance(alpha, float):
            raise TypeError(f"'alpha' must be '<float>', but got {type(alpha).__name__}")
        if not 0.0 <= alpha <= 1.0:
            raise ValueError(f"'alpha' must be [0, 1.0]")
        self._alpha = alpha

    @property
    def fill(self) -> bool:
        """
        bool: Gets whether shapes should be filled with color.

        Returns:
            bool: True for filled shapes, False for outline-only rendering.
        """
        return self._fill

    @fill.setter
    def fill(self, fill: bool) -> None:
        """
        Sets the fill mode for shapes.

        Args:
            fill (bool): Whether to fill shapes with color.

        Raises:
            TypeError: If fill is not a boolean.
        """
        if not isinstance(fill, bool):
            raise TypeError(f"'fill' must be '<bool>', but got {type(fill).__name__}")
        self._fill = fill

    @property
    def show(self) -> bool:
        """
        bool: Gets whether visualization rendering is globally enabled.

        Returns:
            bool: True if rendering is enabled, False to bypass all drawing operations.
        """
        return self._show

    @show.setter
    def show(self, show: bool) -> None:
        """
        Sets the global toggle for visualization rendering.

        Args:
            show (bool): Whether to enable rendering.

        Raises:
            TypeError: If show is not a boolean.

        Note:
            Setting `show=False` is recommended for headless/embedded deployments
            to reduce CPU/GPU load while keeping business logic active.
        """
        if not isinstance(show, bool):
            raise TypeError(f"'show' must be '<bool>', but got {type(show).__name__}")
        self._show = show
