"""MFD parameters state management for VideoReceiverSFP.

Self-contained state manager for MFD category parameters matching ControlPanel behavior.
"""
import logging
import numpy as np
from typing import Dict, Any


class MfdState:
    """Manages MFD parameters (categories, intensities, colors, raw_map) and LUT generation."""
    
    def __init__(self):
        self.mfd_params: Dict[str, Any] = self._initialize_mfd_params()
        self.mfd_lut: np.ndarray = np.zeros((256, 3), dtype=np.uint8)
        self._rebuild_lut()
    
    def _initialize_mfd_params(self) -> Dict[str, Any]:
        """Initialize MFD parameters matching ControlPanel defaults."""
        params = {
            "categories": {
                "Occlusion": {"color": (0, 0, 0), "intensity": 255, "pixels": [0, 1]},
                "Cat A": {"color": (255, 255, 255), "intensity": 255, "pixels": [2]},
                "Cat B": {"color": (255, 255, 255), "intensity": 255, "pixels": [3, 18]},
                "Cat C": {"color": (255, 255, 255), "intensity": 255, "pixels": [4, 5, 6, 16]},
                "Cat C1": {"color": (255, 255, 255), "intensity": 255, "pixels": [7, 8, 9]},
                "Cat C2": {"color": (255, 255, 255), "intensity": 255, "pixels": [10, 11, 12]},
                "Cat C3": {"color": (255, 255, 255), "intensity": 255, "pixels": [13, 14, 15]},
                "Reserved": {"color": (0, 0, 0), "intensity": 255, "pixels": list(range(17, 32))},
            },
            "raw_map_intensity": 255,
        }
        # Create reverse mapping pixel_to_category
        params["pixel_to_category"] = {
            p: cat for cat, data in params["categories"].items() for p in data["pixels"]
        }
        return params
    
    def update_category_intensity(self, category_name: str, intensity_value: int) -> bool:
        """Update intensity for a category and rebuild LUT. Returns True if updated."""
        if category_name not in self.mfd_params["categories"]:
            logging.warning(f"[MfdState] Unknown category: {category_name}")
            return False
        clamped = int(np.clip(intensity_value, 0, 255))
        self.mfd_params["categories"][category_name]["intensity"] = clamped
        self._rebuild_lut()
        return True
    
    def update_category_color(self, category_name: str, bgr_tuple: tuple) -> bool:
        """Update color (BGR) for a category and rebuild LUT. Returns True if updated."""
        if category_name not in self.mfd_params["categories"]:
            logging.warning(f"[MfdState] Unknown category: {category_name}")
            return False
        # ensure BGR tuple with clamping
        new_bgr = tuple(int(np.clip(c, 0, 255)) for c in bgr_tuple)
        self.mfd_params["categories"][category_name]["color"] = new_bgr
        self._rebuild_lut()
        return True
    
    def update_raw_map_intensity(self, intensity_value: int) -> bool:
        """Update raw_map_intensity and rebuild LUT. Returns True if updated."""
        clamped = int(np.clip(intensity_value, 0, 255))
        self.mfd_params["raw_map_intensity"] = clamped
        self._rebuild_lut()
        return True
    
    def _rebuild_lut(self):
        """Rebuild the MFD LUT from current parameters (matches ControlPanel logic)."""
        try:
            params = self.mfd_params
            raw_map_factor = params["raw_map_intensity"] / 255.0
            pixel_map = params["pixel_to_category"]
            categories = params["categories"]
            
            # Build in BGR order (ControlPanel convention)
            bgr_lut = np.zeros((256, 3), dtype=np.uint8)
            
            for idx in range(256):
                cat_name = pixel_map.get(idx)
                if cat_name:
                    cat_data = categories[cat_name]
                    bgr = cat_data["color"]
                    intensity_factor = cat_data["intensity"] / 255.0
                    bgr_lut[idx, 0] = int(np.clip(int(round(float(bgr[0]) * intensity_factor)), 0, 255))
                    bgr_lut[idx, 1] = int(np.clip(int(round(float(bgr[1]) * intensity_factor)), 0, 255))
                    bgr_lut[idx, 2] = int(np.clip(int(round(float(bgr[2]) * intensity_factor)), 0, 255))
                elif 32 <= idx <= 255:
                    raw_intensity = (float(idx) - 32.0) * (255.0 / 223.0)
                    final_gray = int(round(np.clip(raw_intensity * raw_map_factor, 0, 255)))
                    bgr_lut[idx, :] = final_gray
            
            # Convert BGR -> RGB for PIL consumers
            self.mfd_lut = bgr_lut[:, ::-1].copy()
        except Exception:
            logging.exception("[MfdState] Error rebuilding LUT")
            self.mfd_lut = np.zeros((256, 3), dtype=np.uint8)
    
    def get_lut(self) -> np.ndarray:
        """Return current MFD LUT (256x3 RGB uint8)."""
        return self.mfd_lut
