"""
dual_norm
案B: 二重正規化型の線検出
背景除算で照明勾配を除去し，
適応的閾値で局所ムラにも対応する二重防壁構成
"""

import time

import cv2
import numpy as np

from common.vision.line_detector import (
    ImageParams,
    LineDetectResult,
    fit_row_centers,
)
from common.vision.morphology import (
    apply_dist_mask,
    apply_iso_closing,
    apply_staged_closing,
    apply_width_filter,
)

# 内訳計測用の累積値
_profile_count: int = 0
_profile_sums: dict[str, float] = {}
_profile_start: float = 0.0
_PROFILE_INTERVAL: float = 3.0


def _profile_reset() -> None:
    """計測値をリセットする"""
    global _profile_count, _profile_sums, _profile_start
    _profile_count = 0
    _profile_sums = {}
    _profile_start = time.time()


def _profile_record(label: str, elapsed: float) -> None:
    """計測値を記録する"""
    _profile_sums[label] = (
        _profile_sums.get(label, 0.0) + elapsed
    )


def _profile_print() -> None:
    """計測結果を出力する"""
    global _profile_count, _profile_start
    if _profile_count == 0:
        return
    elapsed = time.time() - _profile_start
    if elapsed < _PROFILE_INTERVAL:
        return
    parts = []
    for label, total in _profile_sums.items():
        avg = total / _profile_count * 1000.0
        parts.append(f"{label}={avg:.1f}ms")
    print(f"Pi: dual_norm内訳({_profile_count}f) "
          + " ".join(parts))
    _profile_reset()


def detect_dual_norm(
    frame: np.ndarray, params: ImageParams,
) -> LineDetectResult:
    """案B: 二重正規化型"""
    global _profile_count

    if _profile_count == 0 and not _profile_sums:
        _profile_reset()

    # 背景除算正規化
    t0 = time.time()
    bg_k = params.bg_blur_ksize | 1
    bg = cv2.GaussianBlur(
        frame, (bg_k, bg_k), 0,
    )
    normalized = (
        frame.astype(np.float32) * 255.0
        / (bg.astype(np.float32) + 1.0)
    )
    normalized = np.clip(
        normalized, 0, 255,
    ).astype(np.uint8)
    t1 = time.time()
    _profile_record("背景除算", t1 - t0)

    # 適応的閾値（ガウシアン，BINARY_INV）
    block = max(params.adaptive_block | 1, 3)
    binary = cv2.adaptiveThreshold(
        normalized, 255,
        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
        cv2.THRESH_BINARY_INV,
        block, params.adaptive_c,
    )

    # 固定閾値との AND（有効時のみ）
    if params.global_thresh > 0:
        _, global_mask = cv2.threshold(
            normalized, params.global_thresh,
            255, cv2.THRESH_BINARY_INV,
        )
        binary = cv2.bitwise_and(binary, global_mask)
    t2 = time.time()
    _profile_record("閾値処理", t2 - t1)

    # 段階クロージング or 等方クロージング
    if params.stage_min_area > 0:
        binary = apply_staged_closing(
            binary,
            params.stage_close_small,
            params.stage_min_area,
            params.stage_close_large,
        )
    else:
        binary = apply_iso_closing(
            binary, params.iso_close_size,
        )

    # 距離変換マスク + 幅フィルタ
    binary = apply_dist_mask(
        binary, params.dist_thresh,
    )
    if params.width_near > 0 and params.width_far > 0:
        binary = apply_width_filter(
            binary,
            params.width_near,
            params.width_far,
            params.width_tolerance,
        )
    t3 = time.time()
    _profile_record("後処理", t3 - t2)

    # 行ごと中心抽出 + フィッティング
    result = fit_row_centers(
        binary, params.min_line_width,
        median_ksize=params.median_ksize,
        neighbor_thresh=params.neighbor_thresh,
        residual_thresh=params.residual_thresh,
    )
    t4 = time.time()
    _profile_record("fitting", t4 - t3)

    _profile_count += 1
    _profile_print()

    return result
