"""MFD palette LUT helpers for VideoReceiverSFP.

Provides a small, self-contained implementation of the MFD LUT
generation and application compatible with the ControlPanel defaults.

`build_mfd_lut()` returns an (256,3) uint8 array in RGB order suitable
for `Image.fromarray(..., mode='RGB')`.
"""
from typing import Optional
import numpy as np


def build_mfd_lut(raw_map_intensity: int = 255) -> np.ndarray:
    """Build a 256x3 RGB LUT for MFD indices using the same defaults
    ControlPanel uses, but return values in RGB order suitable for PIL.

    The ControlPanel `update_mfd_lut` builds an array in BGR order from
    category definitions. We replicate the category/pixel mapping and
    intensity calculation, then convert the resulting BGR -> RGB so the
    LUT can be used directly with PIL `Image.fromarray(..., mode='RGB')`.
    """
    # Build in BGR order following ControlPanel logic
    bgr_lut = np.zeros((256, 3), dtype=np.uint8)

    # Categories as defined in controlpanel.app_state._initialize_mfd_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))},
    }

    pixel_to_category = {p: cat for cat, data in categories.items() for p in data["pixels"]}

    raw_map_factor = float(raw_map_intensity) / 255.0

    for idx in range(256):
        cat_name = pixel_to_category.get(idx)
        if cat_name:
            cat_data = categories[cat_name]
            bgr = cat_data["color"]
            intensity_factor = cat_data["intensity"] / 255.0
            b = int(np.clip(int(round(float(bgr[0]) * intensity_factor)), 0, 255))
            g = int(np.clip(int(round(float(bgr[1]) * intensity_factor)), 0, 255))
            r = int(np.clip(int(round(float(bgr[2]) * intensity_factor)), 0, 255))
            bgr_lut[idx, 0] = b
            bgr_lut[idx, 1] = g
            bgr_lut[idx, 2] = r
        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, final_gray, final_gray)

    # Convert BGR -> RGB for PIL consumers
    rgb_lut = bgr_lut[:, ::-1].copy()
    return rgb_lut


def apply_mfd_lut(indices: np.ndarray, lut: Optional[np.ndarray]) -> Optional[np.ndarray]:
    """Map an indices array (H,W) to an RGB image (H,W,3) using the LUT.

    Returns a uint8 NumPy array or None on error.
    """
    if lut is None:
        return None
    try:
        # Use NumPy advanced indexing to map (H,W) -> (H,W,3)
        rgb = lut[indices]
        return rgb
    except Exception:
        return None
