"""tab_camera.py - 카메라 측정 탭"""

import os
import time
from datetime import datetime
from pathlib import Path
from typing import Optional

import cv2
import numpy as np
from PySide6.QtCore import QPointF, QRectF, Qt, QTimer
from PySide6.QtGui import QColor, QFontMetrics, QImage, QMouseEvent, QPainter, QPen, QPixmap, QWheelEvent
from PySide6.QtWidgets import (
    QGroupBox, QHBoxLayout, QLabel, QMessageBox, QPushButton,
    QSlider, QSplitter, QVBoxLayout, QWidget
)

from ConfigReader import GlobalConfig
from utils.config_log import logger as log
from project.window_tab_browser import TabApp

from .ImageReader import CenterDetector
from .NCPointView import CamPointMap
from .SlideToggle import LabeledToggle
from .sim_camera import SimCameraDevice

# 상수
SIM_FLAG = False
ELAPSED_UPDATE_MS = 3000
MIN_ZOOM, MAX_ZOOM, ZOOM_DELTA = 0.1, 10.0, 0.1

BUTTON_STYLES = {
    'ready': ("#4CAF50", "#45a049", "#3d8b40"),
    'measuring': ("#FF9800", "#F57C00", "#E65100"),
    'complete': ("#f44336", "#da190b", "#c2170a")
}


class ZoomView(QWidget):
    """확대/축소 이미지 뷰"""
    WIDGET_STYLE = "ZoomView { background-color: #222; border: 2px solid #666; border-radius: 5px; color: #888; }"
    BUTTON_STYLE = """QPushButton { background-color: rgba(85,85,85,180); color: white; border: 2px solid rgba(102,102,102,200);
        border-radius: 5px; font-size: 12px; font-weight: bold; padding: 5px; }
        QPushButton:hover { background-color: rgba(102,102,102,220); }
        QPushButton:pressed { background-color: rgba(68,68,68,220); }"""

    CAMERA_BUTTON_STYLE_CONNECTED = """QPushButton { background-color: rgba(76,175,80,180); color: white; border: 2px solid rgba(102,102,102,200);
        border-radius: 5px; font-size: 14px; padding: 5px; }"""
    CAMERA_BUTTON_STYLE_DISCONNECTED = """QPushButton { background-color: rgba(244,67,54,180); color: white; border: 2px solid rgba(102,102,102,200);
        border-radius: 5px; font-size: 14px; padding: 5px; }"""

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setMinimumSize(640, 480)
        self.setStyleSheet(self.WIDGET_STYLE)
        self.setMouseTracking(True)

        # 카메라 상태 버튼
        self.camera_status_button = QPushButton("CAM", self)
        self.camera_status_button.setFixedSize(70, 30)
        self.camera_status_button.setFocusPolicy(Qt.NoFocus)
        self.camera_status_button.setStyleSheet(self.CAMERA_BUTTON_STYLE_DISCONNECTED)
        self.camera_status_button.setToolTip("카메라 연결 안됨")
        self.camera_status_button.raise_()

        self.full_button = QPushButton("Full", self)
        self.full_button.setFixedSize(60, 30)
        self.full_button.setCursor(Qt.PointingHandCursor)
        self.full_button.setFocusPolicy(Qt.NoFocus)
        self.full_button.setStyleSheet(self.BUTTON_STYLE)
        self.full_button.clicked.connect(self.fit_to_screen)
        self.full_button.raise_()

        self.original_pixmap: Optional[QPixmap] = None
        self.display_text = "카메라 연결 후 이미지가 표시됩니다"
        self.show_crosshair = True
        self.zoom_factor = 1.0
        self.pan_offset = QPointF(0, 0)
        self.is_panning = False
        self.last_mouse_pos = QPointF()
        self.is_camera_connected = False

    def set_crosshair_visible(self, visible: bool):
        if self.show_crosshair != visible:
            self.show_crosshair = visible
            self.update()

    def set_image(self, image: np.ndarray):
        if image is None or image.size == 0:
            self.original_pixmap = None
        else:
            try:
                img_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) if len(image.shape) == 3 else image
                h, w = img_rgb.shape[:2]
                bpl = w * (3 if len(img_rgb.shape) == 3 else 1)
                fmt = QImage.Format_RGB888 if len(img_rgb.shape) == 3 else QImage.Format_Grayscale8
                self.original_pixmap = QPixmap.fromImage(QImage(img_rgb.data, w, h, bpl, fmt))
            except Exception as e:
                log.error(f"[ZoomView] 이미지 변환 실패: {e}")
        self.update()

    def reset_view(self):
        self.zoom_factor = 1.0
        self.pan_offset = QPointF(0, 0)
        self.update()

    def fit_to_screen(self):
        if self.original_pixmap and not self.original_pixmap.isNull():
            scale = min(self.width() / self.original_pixmap.width(), self.height() / self.original_pixmap.height())
            self.zoom_factor = max(MIN_ZOOM, min(scale, MAX_ZOOM))
            self.pan_offset = QPointF(0, 0)
            self.update()

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self.full_button.move(self.width() - 70, 10)
        self.camera_status_button.move(self.width() - 150, 10)

    def set_camera_status(self, is_connected: bool):
        """카메라 연결 상태 업데이트"""
        self.is_camera_connected = is_connected
        if is_connected:
            self.camera_status_button.setStyleSheet(self.CAMERA_BUTTON_STYLE_CONNECTED)
            self.camera_status_button.setToolTip("카메라 연결됨")
        else:
            self.camera_status_button.setStyleSheet(self.CAMERA_BUTTON_STYLE_DISCONNECTED)
            self.camera_status_button.setToolTip("카메라 연결 안됨")
        self.update()

    def wheelEvent(self, event: QWheelEvent):
        if not self.original_pixmap:
            return
        mouse_pos = event.position()
        scene_before = self._widget_to_scene(mouse_pos)
        zoom_delta = ZOOM_DELTA if event.angleDelta().y() > 0 else -ZOOM_DELTA
        self.zoom_factor = max(MIN_ZOOM, min(self.zoom_factor + zoom_delta, MAX_ZOOM))
        scene_after = self._widget_to_scene(mouse_pos)
        self.pan_offset -= (scene_after - scene_before)
        self.update()
        event.accept()

    def mousePressEvent(self, event: QMouseEvent):
        if self.full_button.geometry().contains(event.pos()):
            event.ignore()
            return
        if event.button() == Qt.LeftButton and self.original_pixmap:
            self.is_panning = True
            self.last_mouse_pos = event.position()
            self.setCursor(Qt.ClosedHandCursor)
            event.accept()

    def mouseMoveEvent(self, event: QMouseEvent):
        if self.full_button.geometry().contains(event.pos()):
            return
        if self.is_panning:
            self.pan_offset += event.position() - self.last_mouse_pos
            self.last_mouse_pos = event.position()
            self.update()
            event.accept()
        elif self.original_pixmap:
            self.setCursor(Qt.OpenHandCursor)

    def mouseReleaseEvent(self, event: QMouseEvent):
        if event.button() == Qt.LeftButton:
            self.is_panning = False
            self.setCursor(Qt.OpenHandCursor if self.original_pixmap else Qt.ArrowCursor)
            event.accept()

    def draw_crosshair(self, painter: QPainter, imgBox: QRectF):
        if not self.original_pixmap or imgBox.isNull():
            return
        cfg = GlobalConfig.instance()
        pixels_per_mm = cfg.getConfigValue("core.pixels_per_mm", 110.0)
        circle_mark_size_mm = cfg.getConfigValue("core.circle_mark_size", 1.75)
        center_offset = cfg.getConfigValue("core.center_offset", [0, 0])

        circle_diameter = circle_mark_size_mm * pixels_per_mm * self.zoom_factor
        cx = imgBox.center().x() + center_offset[0]
        cy = imgBox.center().y() + center_offset[1]

        painter.setPen(QPen(QColor(255, 0, 0), 2))
        painter.drawEllipse(QPointF(cx, cy), circle_diameter / 2, circle_diameter / 2)

        painter.setPen(QPen(QColor(255, 0, 0), 1))
        cross_size = 30 * self.zoom_factor
        painter.drawLine(cx - cross_size, cy, cx + cross_size, cy)
        painter.drawLine(cx, cy - cross_size, cx, cy + cross_size)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setRenderHint(QPainter.SmoothPixmapTransform)
        painter.fillRect(self.rect(), QColor(34, 34, 34))

        if not self.original_pixmap or self.original_pixmap.isNull():
            painter.setPen(QColor(136, 136, 136))
            painter.drawText(self.rect(), Qt.AlignCenter, self.display_text)
        else:
            center = QPointF(self.width() / 2, self.height() / 2)
            w = self.original_pixmap.width() * self.zoom_factor
            h = self.original_pixmap.height() * self.zoom_factor
            x = center.x() - w / 2 + self.pan_offset.x()
            y = center.y() - h / 2 + self.pan_offset.y()
            imgBox = QRectF(x, y, w, h)
            painter.drawPixmap(imgBox, self.original_pixmap, QRectF(self.original_pixmap.rect()))
            if self.show_crosshair:
                self.draw_crosshair(painter, imgBox)

        # 카메라 연결 안됨 배너
        if not self.is_camera_connected:
            banner_text = "카메라가 연결되지 않았습니다."
            font = painter.font()
            font.setPointSize(16)
            font.setBold(True)
            painter.setFont(font)

            # 배너 배경
            fm = QFontMetrics(font)
            text_width = fm.horizontalAdvance(banner_text)
            text_height = fm.height()
            padding_h, padding_v = 40, 20
            border_radius = 10
            banner_rect = QRectF(
                (self.width() - text_width) / 2 - padding_h,
                (self.height() - text_height) / 2 - padding_v,
                text_width + padding_h * 2,
                text_height + padding_v * 2
            )
            painter.setBrush(QColor(180, 50, 50, 200))
            painter.setPen(QPen(QColor(255, 255, 255), 2))
            painter.drawRoundedRect(banner_rect, border_radius, border_radius)

            # 배너 텍스트
            painter.setPen(QColor(255, 255, 255))
            painter.drawText(self.rect(), Qt.AlignCenter, banner_text)

        painter.end()

    def _widget_to_scene(self, pos: QPointF) -> QPointF:
        if not self.original_pixmap:
            return QPointF()
        center = QPointF(self.width() / 2, self.height() / 2)
        offset = pos - center
        return QPointF((offset.x() - self.pan_offset.x()) / self.zoom_factor,
                       (offset.y() - self.pan_offset.y()) / self.zoom_factor)


class CameraView(ZoomView):
    pass


class TabCamera(TabApp):
    """카메라 측정 탭"""

    def __init__(self, parent=None, param=None):
        super().__init__(parent)
        self.measurement_data = param or {}
        self.project_name = self.measurement_data.get('project', 'Unknown')
        self.current_path = self.measurement_data.get('current_path', '')
        self.nc_list = self.measurement_data.get('nc_list', [])

        self.is_connected = False
        self.save_enabled = False
        self.trigger_mode = False
        self.frame_count = 0
        self.is_measuring = False
        self.current_session_path = None
        self.nc_points = []
        self.measurement_start_time = None

        log.info(f"[CameraTab] 초기화 ({self.project_name}, NC: {len(self.nc_list)}개)")

        if self.measurement_data:
            self.set_tab_title(f"측정: {self.project_name}")

        self.detector = CenterDetector()
        self._init_camera()
        self._init_image_save()

        self.elapsed_timer = QTimer(self)
        self.elapsed_timer.setInterval(ELAPSED_UPDATE_MS)
        self.elapsed_timer.timeout.connect(self._update_elapsed_time)

        self._load_nc_points()
        self._setup_ui()

    # =========================================================================
    # 초기화
    # =========================================================================
    def _init_camera(self):
        if SIM_FLAG:
            log.info("[CameraTab] 시뮬레이션 모드")
            self.camera = SimCameraDevice()
        else:
            log.info("[CameraTab] 실제 카메라 모드")
            from .imi_camera import imi_camera
            self.camera = imi_camera()
            self.camera.image_received.connect(self._on_camera_image_received)
            self.camera.status_changed.connect(self._on_camera_status_changed)

    def _init_image_save(self):
        from .ImageSave import ImageSave
        self.image_save = ImageSave()

    def _load_nc_points(self):
        if not self.nc_list:
            return
        try:
            from nc_reader import nc_reader
            reader = nc_reader(self.nc_list[0])
            detailed_points = reader.read_detailed(firstOnly=True)
            self.nc_points = [
                {'x': p['x']['value'], 'y': p['y']['value'],
                 'z': p.get('z', {}).get('value', 0.0),
                 'a': p.get('a', {}).get('value', 0.0),
                 'c': p.get('c', {}).get('value', 0.0)}
                for p in detailed_points
            ]
            log.info(f"[CameraTab] NC 포인트: {len(self.nc_points)}개")
        except Exception as e:
            log.error(f"[CameraTab] NC 포인트 로드 실패: {e}")
            self.nc_points = []

    # =========================================================================
    # UI 구성
    # =========================================================================
    def _setup_ui(self):
        main_layout = QHBoxLayout(self)
        main_layout.setContentsMargins(5, 5, 5, 5)
        main_layout.setSpacing(5)

        splitter = QSplitter(Qt.Horizontal)
        splitter.addWidget(self._create_camera_view())
        splitter.addWidget(self._create_right_panel())
        splitter.setSizes([700, 300])
        main_layout.addWidget(splitter)

        self.setStyleSheet("""
            QWidget { background-color: #333; color: white; }
            QPushButton { background-color: #555; color: white; border: 1px solid #666; padding: 8px 15px; border-radius: 4px; font-size: 13px; }
            QPushButton:hover { background-color: #666; }
            QPushButton:disabled { background-color: #444; color: #888; }
            QGroupBox { border: 1px solid #666; border-radius: 5px; margin-top: 10px; padding-top: 10px; font-weight: bold; font-size: 14px; }
            QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; padding: 0 5px; color: #4CAF50; }
            QLabel { color: white; font-size: 13px; }
            QComboBox, QSpinBox, QLineEdit { background-color: #444; color: white; border: 1px solid #666; padding: 5px; border-radius: 3px; }
        """)

    def _create_camera_view(self) -> QWidget:
        widget = QWidget()
        layout = QVBoxLayout(widget)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(5)
        self.camera_view = CameraView()
        layout.addWidget(self.camera_view, stretch=1)
        return widget

    def _create_right_panel(self) -> QWidget:
        widget = QWidget()
        layout = QVBoxLayout(widget)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(5)

        # 중심화면 그룹
        center_group = QGroupBox("중심화면")
        center_layout = QVBoxLayout(center_group)
        center_layout.setContentsMargins(5, 10, 5, 5)
        center_layout.setSpacing(3)

        # 측정 버튼
        self.measure_button = QPushButton("측정")
        self._set_measure_button_style('ready')
        self.measure_button.clicked.connect(self._on_measure_button_clicked)
        center_layout.addWidget(self.measure_button)

        # 경과 시간
        self.elapsed_time_label = QLabel("")
        self.elapsed_time_label.setStyleSheet("color: #FFA726; font-weight: bold; font-size: 12px;")
        self.elapsed_time_label.setVisible(False)
        center_layout.addWidget(self.elapsed_time_label)

        # 프레임/트리거/3점촬영 컨트롤
        control_layout = QHBoxLayout()
        self.frame_count_label = QLabel("Frame: 0")
        self.frame_count_label.setStyleSheet("color: white; font-weight: bold;")
        control_layout.addWidget(self.frame_count_label)

        control_layout.addStretch()  # 좌측(Frame) / 우측(3점) 분리

        self.cut3_toggle = LabeledToggle("3점:")
        cfg = GlobalConfig.instance()
        self.cut3_toggle.setChecked(cfg.getConfigValue("core.image_per_info", 1) == 3)
        self.cut3_toggle.toggled.connect(self._on_cut3_toggle_changed)
        control_layout.addWidget(self.cut3_toggle)
        center_layout.addLayout(control_layout)

        # 노출 슬라이더
        expose_layout = QHBoxLayout()
        self.expose_label = QLabel("Expose:")
        self.expose_label.setMinimumWidth(60)
        expose_layout.addWidget(self.expose_label)

        self.expose_slider = QSlider(Qt.Horizontal)
        self.expose_slider.setRange(5000, 30000)
        self.expose_slider.setValue(5000)
        self.expose_slider.setTickPosition(QSlider.TicksBelow)
        self.expose_slider.setTickInterval(2000)
        self.expose_slider.setEnabled(False)
        self.expose_slider.valueChanged.connect(self._on_expose_changed)
        expose_layout.addWidget(self.expose_slider)

        self.expose_value_label = QLabel("5,000")
        self.expose_value_label.setMinimumWidth(50)
        self.expose_value_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        expose_layout.addWidget(self.expose_value_label)
        center_layout.addLayout(expose_layout)

        # 중심 확대 뷰
        self.center_zoom_view = QLabel("중심 영역 확대")
        self.center_zoom_view.setAlignment(Qt.AlignCenter)
        self.center_zoom_view.setMinimumHeight(150)
        self.center_zoom_view.setStyleSheet("QLabel { background-color: #222; border: 2px solid #666; border-radius: 5px; color: #888; }")
        center_layout.addWidget(self.center_zoom_view, stretch=1)
        layout.addWidget(center_group, stretch=1)

        # 측정 정보 그룹
        if self.measurement_data:
            info_group = QGroupBox("측정 정보")
            info_layout = QVBoxLayout(info_group)
            info_layout.setSpacing(3)

            project_label = QLabel(f"프로젝트: {self.project_name}")
            project_label.setStyleSheet("color: #CCCCCC;")
            info_layout.addWidget(project_label)

            path_label = QLabel(f"경로: {self.current_path}")
            path_label.setStyleSheet("color: #CCCCCC;")
            path_label.setWordWrap(True)
            info_layout.addWidget(path_label)

            nc_filenames = [os.path.basename(nc) for nc in self.nc_list]
            nc_label = QLabel(f"NC 파일: {', '.join(nc_filenames)}")
            nc_label.setStyleSheet("color: #CCCCCC;")
            nc_label.setWordWrap(True)
            info_layout.addWidget(nc_label)
            layout.addWidget(info_group)

        # NC 포인트 뷰
        nc_group = QGroupBox("Point")
        nc_layout = QVBoxLayout(nc_group)
        self.nc_point_view = CamPointMap()
        if self.nc_points and self.nc_list:
            self.nc_point_view.set_nc_points(self.nc_points, os.path.basename(self.nc_list[0]))
        nc_layout.addWidget(self.nc_point_view)
        layout.addWidget(nc_group, stretch=1)

        return widget

    # =========================================================================
    # 탭 라이프사이클
    # =========================================================================
    def can_close(self) -> bool:
        if self.is_measuring:
            reply = QMessageBox.question(
                self, '측정 중', '측정이 진행 중입니다. 탭을 닫으시겠습니까?',
                QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
            if reply == QMessageBox.No:
                return False
            self.image_save.end()
            self._disconnect_camera()
        return True

    def on_tab_activated(self):
        self.monitor_mode()

    def on_tab_deactivated(self):
        if self.is_connected and not self.is_measuring:
            log.info('[CameraTab] 탭 비활성화: 카메라 연결 해제')
            self._disconnect_camera()

    def on_session_selected(self, project, nc_filename: str, session):
        project_name = project.name if hasattr(project, 'name') else 'Unknown'
        log.info(f"[CameraTab] 세션 선택: {project_name}/{nc_filename}/{session.session_name}")

        self.current_session_path = None
        if hasattr(session, 'session_path') and session.session_path:
            session_dir = Path(session.session_path)
            self.current_session_path = str(session_dir)
            if session_dir.exists() and hasattr(self.camera, 'set_image_path'):
                self.camera.set_image_path(str(session_dir))

    # =========================================================================
    # 측정 모드
    # =========================================================================
    def monitor_mode(self):
        if self.is_measuring:
            return
        log.info('[CameraTab] 모니터 모드 진입')
        if not self.is_connected and self._connect_camera():
            self.trigger_mode = False
            if not SIM_FLAG:
                self.camera.set_trigger_mode(False)

    def _on_measure_button_clicked(self):
        if not self.is_measuring:
            self._start_measurement()
        else:
            self._stop_measurement()

    def _start_measurement(self):
        self.is_measuring = True
        self.frame_count = 0
        self.frame_count_label.setText("Frame: 0")

        if not self._connect_camera():
            QMessageBox.warning(self, "연결 실패", "카메라 연결에 실패했습니다.")
            self.is_measuring = False
            self.image_save.end()
            return

        self.camera.image_received.disconnect(self._on_camera_image_received)
        self.camera.stop_stream()
        time.sleep(0.5)

        self.trigger_mode = True
        if not SIM_FLAG:
            self.camera.set_trigger_mode(True)
        self.camera.start_stream()
        self.camera.image_received.connect(self._on_camera_image_received)

        session_path = self._create_session_path()
        if session_path:
            self.current_session_path = session_path
            if self.image_save.start(session_path):
                self.save_enabled = True
                log.info(f"[CameraTab] 측정 저장 시작: {session_path}")

        self._set_exposure_visible(False)
        if self.nc_point_view:
            self.nc_point_view.set_current_count(0)

        self.measurement_start_time = datetime.now()
        self.elapsed_time_label.setText("경과시간: 00:00:00")
        self.elapsed_time_label.setVisible(True)
        self.elapsed_timer.start()

        total = len(self.nc_points)
        self.measure_button.setText(f"측정중(0/{total})")
        self._set_measure_button_style('measuring')
        log.info(f"[CameraTab] 측정 시작: {total}개")

    def _stop_measurement(self):
        self.is_measuring = False
        self.measure_button.setText("측정")
        self._set_measure_button_style('ready')
        self._disconnect_camera()

        self._set_exposure_visible(True)
        self.elapsed_timer.stop()
        self.elapsed_time_label.setVisible(False)
        self.measurement_start_time = None
        self.image_save.end()

        log.info('[CameraTab] 측정 종료')
        self.monitor_mode()

    def _auto_stop_measurement(self):
        if self.is_measuring:
            self._stop_measurement()
            log.info("[CameraTab] 측정 자동 종료")

    # =========================================================================
    # 카메라 제어
    # =========================================================================
    def _connect_camera(self) -> bool:
        log.info(f"[CameraTab] 카메라 연결 (SIM={SIM_FLAG})")
        try:
            if self.camera.open():
                self.is_connected = True
                self._update_controls_enabled(True)
                # 카메라에서 노출값 가져와서 슬라이더에 설정
                if hasattr(self.camera, 'get_exposure'):
                    exposure = self.camera.get_exposure()
                    self.expose_slider.setValue(exposure)
                    self.expose_value_label.setText(f"{exposure:,}")
                if SIM_FLAG and hasattr(self.camera, 'start_grabbing'):
                    self.camera.start_grabbing()
                log.info("[CameraTab] 카메라 연결 성공")
                return True
        except Exception as e:
            log.exception(f"[CameraTab] 카메라 연결 실패: {e}")
        return False

    def _disconnect_camera(self):
        try:
            self.camera.close()
            self.is_connected = False
            self._update_controls_enabled(False)
            self.center_zoom_view.setText("중심 영역 확대")
            self.center_zoom_view.setPixmap(QPixmap())
            log.info("[CameraTab] 카메라 연결 해제")
        except Exception as e:
            log.exception(f"[CameraTab] 카메라 해제 실패: {e}")

    # =========================================================================
    # 이미지 콜백
    # =========================================================================
    def _on_camera_status_changed(self, status: str):
        """카메라 상태 변경 처리"""
        is_connected = (status == "connected")
        if hasattr(self, 'camera_view'):
            self.camera_view.set_camera_status(is_connected)

    def _on_camera_image_received(self, image: np.ndarray):
        self.camera_view.set_image(image)
        self._update_center_zoom(image)

        if not self.is_measuring:
            return

        self.image_info = self.detector.build_info(self.frame_count, image, self.current_session_path)

        if self.save_enabled and self.image_info:
            image_per_info = GlobalConfig.instance().getConfigValue("core.image_per_info", 1)
            if self.image_save.save_image(image, self.image_info, image_per_info):
                self._on_frame_saved()

    def _on_frame_saved(self):
        self.frame_count = self.image_save.frame_count
        self.frame_count_label.setText(f"Frame: {self.frame_count}")

        total = len(self.nc_points)
        multiplier = GlobalConfig.instance().getConfigValue("core.image_per_info", 1)

        if self.nc_point_view and self.frame_count <= len(self.nc_point_view.nc_points) * multiplier:
            self.nc_point_view.set_current_count(self.frame_count // multiplier)

        is_complete = self.frame_count >= total * multiplier
        text = "측정종료" if is_complete else f"측정중({self.frame_count}/{total})"
        self.measure_button.setText(text)
        self._set_measure_button_style('complete' if is_complete else 'measuring')

        if is_complete:
            log.info(f"[CameraTab] 측정 완료: {self.frame_count}/{total}")
            QTimer.singleShot(100, self._auto_stop_measurement)

    def _update_center_zoom(self, image):
        if image is None or image.size == 0:
            return
        try:
            h, w = image.shape[:2]
            cw, ch = w // 4, h // 4
            x, y = (w - cw) // 2, (h - ch) // 2
            zoomed = cv2.resize(image[y:y+ch, x:x+cw], (cw * 2, ch * 2), interpolation=cv2.INTER_LINEAR)
            rgb = cv2.cvtColor(zoomed, cv2.COLOR_BGR2RGB) if len(zoomed.shape) == 3 else zoomed
            zh, zw = rgb.shape[:2]
            bpl = zw * (3 if len(rgb.shape) == 3 else 1)
            fmt = QImage.Format_RGB888 if len(rgb.shape) == 3 else QImage.Format_Grayscale8
            pixmap = QPixmap.fromImage(QImage(rgb.data, zw, zh, bpl, fmt))
            self.center_zoom_view.setPixmap(pixmap.scaled(self.center_zoom_view.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
        except Exception as e:
            log.error(f"[CameraTab] 중심 확대 실패: {e}")

    # =========================================================================
    # UI 이벤트 핸들러
    # =========================================================================
    def _on_cut3_toggle_changed(self, checked: bool):
        value = 3 if checked else 1
        GlobalConfig.instance().setConfigValue("core.image_per_info", value)
        log.info(f"[CameraTab] 3점촬영: {value}")

    def _on_expose_changed(self, value: int):
        self.expose_value_label.setText(f"{value:,}")
        if self.is_connected:
            self.camera.set_exposure(value)

    # =========================================================================
    # 유틸리티
    # =========================================================================
    def _create_session_path(self):
        if not self.current_path:
            return None
        try:
            path_parts = self.current_path.replace('\\', '/').split('/')
            if 'input' in path_parts:
                path_parts[path_parts.index('input')] = 'data'
                data_path = '/'.join(path_parts)
            else:
                data_path = os.path.join(self.current_path, 'data')

            os.makedirs(data_path, exist_ok=True)
            session_name = datetime.now().strftime('%Y%m%d_%H%M%S')
            session_path = os.path.join(data_path, session_name)
            os.makedirs(session_path, exist_ok=True)
            log.info(f"[CameraTab] 세션 경로: {session_path}")
            return session_path
        except Exception as e:
            log.error(f"[CameraTab] 세션 경로 생성 실패: {e}")
            return None

    def _update_elapsed_time(self):
        if not self.measurement_start_time or not self.is_measuring:
            return
        elapsed = datetime.now() - self.measurement_start_time
        total_sec = int(elapsed.total_seconds())
        h, m, s = total_sec // 3600, (total_sec % 3600) // 60, total_sec % 60
        self.elapsed_time_label.setText(f"경과시간: {h:02d}:{m:02d}:{s:02d}")

    def _set_measure_button_style(self, state: str):
        color, hover, pressed = BUTTON_STYLES.get(state, BUTTON_STYLES['ready'])
        self.measure_button.setStyleSheet(f"""
            QPushButton {{ background-color: {color}; color: white; font-weight: bold; padding: 8px 20px; border-radius: 5px; }}
            QPushButton:hover {{ background-color: {hover}; }}
            QPushButton:pressed {{ background-color: {pressed}; }}
        """)

    def _set_exposure_visible(self, visible: bool):
        self.expose_label.setVisible(visible)
        self.expose_slider.setVisible(visible)
        self.expose_slider.setEnabled(visible)
        self.expose_value_label.setVisible(visible)

    def _update_controls_enabled(self, enabled: bool):
        self.expose_slider.setEnabled(enabled)
        if not enabled:
            self.frame_count = 0
            self.frame_count_label.setText("Frame: 0")
            self.camera_view.set_image(None)
            self.camera_view.display_text = "카메라 연결 후 이미지가 표시됩니다"
            self.camera_view.update()
