"""
overlay
画像処理の結果をカメラ映像に重ねて描画するモジュール
チェックボックスで個別に ON/OFF できる
"""

from dataclasses import dataclass

import cv2
import numpy as np

from common import config
from pc.vision import line_detector
from pc.vision.line_detector import LineDetectResult

# 描画色の定義 (BGR)
COLOR_NEAR: tuple = (0, 255, 0)
COLOR_FAR: tuple = (255, 0, 0)
COLOR_CENTER: tuple = (0, 255, 255)
COLOR_TEXT: tuple = (255, 255, 255)

# 二値化オーバーレイの不透明度
BINARY_OPACITY: float = 0.4


@dataclass
class OverlayFlags:
    """オーバーレイ表示項目のフラグ

    Attributes:
        binary: 二値化画像の半透明表示
        near_region: 近方領域の枠
        far_region: 遠方領域の枠
        centroids: 重心マーカー
        center_line: 画像中心線
        error_text: 偏差の数値表示
    """
    binary: bool = False
    near_region: bool = False
    far_region: bool = False
    centroids: bool = False
    center_line: bool = False
    error_text: bool = False


def draw_overlay(
    frame: np.ndarray,
    result: LineDetectResult | None,
    flags: OverlayFlags,
) -> np.ndarray:
    """カメラ映像にオーバーレイを描画する

    Args:
        frame: 元の BGR カメラ画像
        result: 線検出の結果（None の場合はオーバーレイなし）
        flags: 表示項目のフラグ

    Returns:
        オーバーレイ描画済みの画像
    """
    display = frame.copy()

    if result is None:
        return display

    # 二値化画像の半透明オーバーレイ
    if flags.binary and result.binary_image is not None:
        display = _draw_binary_overlay(
            display, result.binary_image,
        )

    # 近方領域の枠
    if flags.near_region:
        cv2.rectangle(
            display,
            (0, line_detector.NEAR_Y_START),
            (
                config.FRAME_WIDTH - 1,
                line_detector.NEAR_Y_END - 1,
            ),
            COLOR_NEAR, 1,
        )

    # 遠方領域の枠
    if flags.far_region:
        cv2.rectangle(
            display,
            (0, line_detector.FAR_Y_START),
            (
                config.FRAME_WIDTH - 1,
                line_detector.FAR_Y_END - 1,
            ),
            COLOR_FAR, 1,
        )

    # 画像中心線
    if flags.center_line:
        center_x = config.FRAME_WIDTH // 2
        cv2.line(
            display,
            (center_x, 0),
            (center_x, config.FRAME_HEIGHT),
            COLOR_CENTER, 1,
        )

    # 重心マーカー
    if flags.centroids:
        if result.near_x is not None:
            near_y = (
                line_detector.NEAR_Y_START
                + line_detector.NEAR_Y_END
            ) // 2
            cv2.circle(
                display,
                (int(result.near_x), near_y),
                6, COLOR_NEAR, -1,
            )
        if result.far_x is not None:
            far_y = (
                line_detector.FAR_Y_START
                + line_detector.FAR_Y_END
            ) // 2
            cv2.circle(
                display,
                (int(result.far_x), far_y),
                6, COLOR_FAR, -1,
            )

    # 偏差の数値表示
    if flags.error_text:
        _draw_error_text(display, result)

    return display


def _draw_binary_overlay(
    frame: np.ndarray,
    binary: np.ndarray,
) -> np.ndarray:
    """二値化画像を半透明で重ねる

    Args:
        frame: 元の BGR 画像
        binary: 二値化画像（グレースケール）

    Returns:
        合成された画像
    """
    # 二値化画像を赤色の BGR に変換
    binary_bgr = np.zeros_like(frame)
    binary_bgr[:, :, 2] = binary

    # 半透明合成
    return cv2.addWeighted(
        frame, 1.0 - BINARY_OPACITY,
        binary_bgr, BINARY_OPACITY, 0,
    )


def _draw_error_text(
    frame: np.ndarray,
    result: LineDetectResult,
) -> None:
    """偏差の数値を画像に描画する

    Args:
        frame: 描画先の画像
        result: 線検出の結果
    """
    near_str = (
        f"near: {result.near_error:+.3f}"
        if result.near_x is not None
        else "near: N/A"
    )
    far_str = (
        f"far: {result.far_error:+.3f}"
        if result.far_x is not None
        else "far: N/A"
    )
    cv2.putText(
        frame, near_str, (5, 15),
        cv2.FONT_HERSHEY_SIMPLEX, 0.4,
        COLOR_TEXT, 1,
    )
    cv2.putText(
        frame, far_str, (5, 30),
        cv2.FONT_HERSHEY_SIMPLEX, 0.4,
        COLOR_TEXT, 1,
    )
