"""Saturation postprocessing filter."""

from typing import Any, Dict, List, Optional

import numpy as np

from wled_controller.core.filters.base import FilterOptionDef, PostprocessingFilter
from wled_controller.core.filters.image_pool import ImagePool
from wled_controller.core.filters.registry import FilterRegistry


@FilterRegistry.register
class SaturationFilter(PostprocessingFilter):
    """Adjusts color saturation via luminance blending (integer math)."""

    filter_id = "saturation"
    filter_name = "Saturation"

    def __init__(self, options: Dict[str, Any]):
        super().__init__(options)
        self._i32_buf: Optional[np.ndarray] = None
        self._i32_gray: Optional[np.ndarray] = None

    @classmethod
    def get_options_schema(cls) -> List[FilterOptionDef]:
        return [
            FilterOptionDef(
                key="value",
                label="Saturation",
                option_type="float",
                default=1.0,
                min_value=0.0,
                max_value=2.0,
                step=0.1,
            ),
        ]

    def process_image(self, image: np.ndarray, image_pool: ImagePool) -> Optional[np.ndarray]:
        value = self.options["value"]
        if value == 1.0:
            return None
        h, w, c = image.shape
        shape3 = (h, w, c)
        shape1 = (h, w, 1)
        if self._i32_buf is None or self._i32_buf.shape != shape3:
            self._i32_buf = np.empty(shape3, dtype=np.int32)
            self._i32_gray = np.empty(shape1, dtype=np.int32)
        i32 = self._i32_buf
        gray = self._i32_gray
        sat_i = int(value * 256)
        # Rec.601 luminance: (R*299 + G*587 + B*114) / 1000
        np.copyto(i32, image, casting='unsafe')
        gray[:, :, 0] = (i32[:, :, 0] * 299 + i32[:, :, 1] * 587 + i32[:, :, 2] * 114) // 1000
        # Blend: out = ((256 - sat) * gray + sat * color) >> 8
        i32 *= sat_i
        gray *= (256 - sat_i)
        i32 += gray
        i32 >>= 8
        np.clip(i32, 0, 255, out=i32)
        np.copyto(image, i32, casting='unsafe')
        return None
