"""hik_camera_ros2_driver가 선언하는 파라미터 스펙.

이름/타입/범위는 hik_camera_node.cpp의 declareParameters()와 dynamicParametersCallback()에
선언/처리되는 것과 반드시 일치해야 한다 (그쪽을 바꾸면 여기도 같이 바꿀 것).

sync_role / sync_master_camera_ns는 런타임에 값을 바꿔도 드라이버가 무시한다
(subscription/publisher가 노드 시작 시 initSync()에서 한 번만 만들어짐) — 그래서
readonly=True로 표시해 패널에서는 조회만 하고 편집은 막는다.
"""

import math

SLIDER_STEPS = 1000


PARAM_SPECS = [
    # --- 노출 ---
    dict(name='exposure_auto', type='bool', group='노출', label='자동 노출'),
    dict(name='exposure_time', type='int', group='노출', label='수동 노출시간 [us]',
         min=15, max=100000, log_scale=True),
    dict(name='exposure_auto_target_brightness', type='int', group='노출',
         label='목표 밝기 (온보드 AE)', min=0, max=255),
    dict(name='exposure_auto_min', type='double', group='노출', label='자동 노출 하한 [us]',
         min=15.0, max=100000.0, log_scale=True, decimals=0),
    dict(name='exposure_auto_max', type='double', group='노출', label='자동 노출 상한 [us]',
         min=15.0, max=100000.0, log_scale=True, decimals=0),

    # --- 게인 ---
    dict(name='gain', type='double', group='게인', label='수동 게인 [dB]',
         min=0.0, max=17.0, decimals=1),
    dict(name='gain_auto', type='bool', group='게인', label='자동 게인'),
    dict(name='gain_auto_max_db', type='double', group='게인', label='자동 게인 상한 [dB]',
         min=0.0, max=17.0, decimals=1),

    # --- 소프트웨어 AE / ROI ---
    dict(name='use_software_ae', type='bool', group='소프트웨어 AE / ROI',
         label='소프트웨어 AE 사용'),
    dict(name='ae_roi_top_ratio', type='double', group='소프트웨어 AE / ROI',
         label='측광 제외 상단 비율 (0.5=하단 절반만)', min=0.0, max=0.95, decimals=2),
    dict(name='ae_target_percentile', type='double', group='소프트웨어 AE / ROI',
         label='목표 퍼센타일', min=0.0, max=100.0, decimals=1),
    dict(name='ae_target_dn', type='int', group='소프트웨어 AE / ROI',
         label='목표 DN', min=0, max=255),
    dict(name='ae_saturation_percentile', type='double', group='소프트웨어 AE / ROI',
         label='포화 방지 퍼센타일', min=0.0, max=100.0, decimals=1),
    dict(name='ae_saturation_dn', type='int', group='소프트웨어 AE / ROI',
         label='포화 방지 DN', min=0, max=255),
    dict(name='ae_step_gain', type='double', group='소프트웨어 AE / ROI',
         label='보정 댐핑 (0~1)', min=0.0, max=1.0, decimals=2),

    # --- 동기화 (재시작 필요 — 아래 readonly 참고) ---
    dict(name='sync_role', type='string', group='동기화 (재시작 필요)', label='역할',
         readonly=True),
    dict(name='sync_master_camera_ns', type='string', group='동기화 (재시작 필요)',
         label='마스터 camera_name', readonly=True),
]

PARAM_SPEC_BY_NAME = {spec['name']: spec for spec in PARAM_SPECS}


def value_to_slider(value, vmin, vmax, log_scale):
    """실제 값을 0~SLIDER_STEPS 정수 슬라이더 위치로 변환."""
    value = min(max(value, vmin), vmax)
    if log_scale:
        vmin_eff = max(vmin, 1e-9)
        value_eff = max(value, vmin_eff)
        lo, hi = math.log(vmin_eff), math.log(vmax)
        frac = (math.log(value_eff) - lo) / (hi - lo) if hi > lo else 0.0
    else:
        frac = (value - vmin) / (vmax - vmin) if vmax > vmin else 0.0
    return int(round(frac * SLIDER_STEPS))


def slider_to_value(pos, vmin, vmax, log_scale):
    """슬라이더 위치(0~SLIDER_STEPS)를 실제 값으로 변환."""
    frac = min(max(pos, 0), SLIDER_STEPS) / SLIDER_STEPS
    if log_scale:
        vmin_eff = max(vmin, 1e-9)
        lo, hi = math.log(vmin_eff), math.log(vmax)
        return math.exp(lo + frac * (hi - lo))
    return vmin + frac * (vmax - vmin)
