from engine.effects.types import EffectConfig, EffectContext, EffectPlugin


class TintEffect(EffectPlugin):
    """Tint effect that applies an RGB color overlay to the buffer.

    Uses ANSI escape codes to tint text with the specified RGB values.
    Supports transparency (0-100%) for blending.

    Inlets:
    - r: Red component (0-255)
    - g: Green component (0-255)
    - b: Blue component (0-255)
    - a: Alpha/transparency (0.0-1.0, where 0.0 = fully transparent)
    """

    name = "tint"
    config = EffectConfig(enabled=True, intensity=1.0)

    # Define inlet types for PureData-style typing
    @property
    def inlet_types(self) -> set:
        from engine.pipeline.core import DataType

        return {DataType.TEXT_BUFFER}

    @property
    def outlet_types(self) -> set:
        from engine.pipeline.core import DataType

        return {DataType.TEXT_BUFFER}

    def process(self, buf: list[str], ctx: EffectContext) -> list[str]:
        if not buf:
            return buf

        # Get tint values from effect params or sensors
        r = self.config.params.get("r", 255)
        g = self.config.params.get("g", 255)
        b = self.config.params.get("b", 255)
        a = self.config.params.get("a", 0.3)  # Default 30% tint

        # Clamp values
        r = max(0, min(255, int(r)))
        g = max(0, min(255, int(g)))
        b = max(0, min(255, int(b)))
        a = max(0.0, min(1.0, float(a)))

        if a <= 0:
            return buf

        # Convert RGB to ANSI 256 color
        ansi_color = self._rgb_to_ansi256(r, g, b)

        # Apply tint with transparency effect
        result = []
        for line in buf:
            if not line.strip():
                result.append(line)
                continue

            # Check if line already has ANSI codes
            if "\033[" in line:
                # For lines with existing colors, wrap the whole line
                result.append(f"\033[38;5;{ansi_color}m{line}\033[0m")
            else:
                # Apply tint to plain text lines
                result.append(f"\033[38;5;{ansi_color}m{line}\033[0m")

        return result

    def _rgb_to_ansi256(self, r: int, g: int, b: int) -> int:
        """Convert RGB (0-255 each) to ANSI 256 color code."""
        if r == g == b == 0:
            return 16
        if r == g == b == 255:
            return 231

        # Calculate grayscale
        gray = int((0.299 * r + 0.587 * g + 0.114 * b) / 255 * 24) + 232

        # Calculate color cube
        ri = int(r / 51)
        gi = int(g / 51)
        bi = int(b / 51)
        color = 16 + 36 * ri + 6 * gi + bi

        # Use whichever is closer - gray or color
        gray_dist = abs(r - gray)
        color_dist = (
            (r - ri * 51) ** 2 + (g - gi * 51) ** 2 + (b - bi * 51) ** 2
        ) ** 0.5

        if gray_dist < color_dist:
            return gray
        return color

    def configure(self, config: EffectConfig) -> None:
        self.config = config
