from PyQt5.QtWidgets import (
    QDialog, QFormLayout, QVBoxLayout, QHBoxLayout,
    QPushButton, QDoubleSpinBox, QSpinBox, QMessageBox, QLabel
)
from PyQt5.QtCore import Qt


# (레이블, 셀주소, 위젯종류, min, max, 기본값, 소수점자리)
# D3(노출)은 AcqExposure() 출력 셀 — 읽기 전용, 목록에서 제외
_PARAMS = [
    ("최대 노출 시간",   "F3",  "double", 0.01, 1000.0, 950.0, 3),
    ("목표 이미지 밝기", "G3",  "double", 0.0,  255.0,   50.0, 1),
    ("조명 강도",        "D6",  "int",    0,    100,      70,  0),
    ("초점 위치",        "D14", "int",    0,    999,     139,  0),
]


class ImageSettingsDialog(QDialog):
    def __init__(self, insight_cam, parent=None):
        super().__init__(parent)
        self._cam = insight_cam
        self.setWindowTitle("이미지 설정 — In-Sight 2000C")
        self.setMinimumWidth(360)
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)

        self._widgets   = {}  # 셀주소 → 위젯
        self._originals = {}  # 셀주소 → 로드 시 원본값

        self._build_ui()
        self._load_values()

    # ------------------------------------------------------------------ #
    # UI 구성
    # ------------------------------------------------------------------ #

    def _build_ui(self):
        form = QFormLayout()
        form.setLabelAlignment(Qt.AlignRight)
        form.setRowWrapPolicy(QFormLayout.DontWrapRows)
        form.setHorizontalSpacing(16)
        form.setVerticalSpacing(10)

        # 노출(D3)은 읽기 전용 표시
        self._exposure_label = QLabel("—")
        self._exposure_label.setStyleSheet("color: gray;")
        form.addRow("노출 (밀리초, 읽기 전용):", self._exposure_label)

        for label, cell, kind, lo, hi, default, decimals in _PARAMS:
            if kind == "double":
                w = QDoubleSpinBox()
                w.setRange(lo, hi)
                w.setDecimals(decimals)
                w.setValue(default)
                w.setSingleStep(0.1)
            else:
                w = QSpinBox()
                w.setRange(int(lo), int(hi))
                w.setValue(int(default))

            w.setMinimumWidth(120)
            self._widgets[cell] = w
            form.addRow(label, w)

        btn_ok = QPushButton("확인")
        btn_ok.setDefault(True)
        btn_ok.clicked.connect(self._on_ok)

        btn_cancel = QPushButton("취소")
        btn_cancel.clicked.connect(self.reject)

        btn_row = QHBoxLayout()
        btn_row.addStretch()
        btn_row.addWidget(btn_ok)
        btn_row.addWidget(btn_cancel)

        root = QVBoxLayout(self)
        root.addLayout(form)
        root.addSpacing(8)
        root.addLayout(btn_row)

    # ------------------------------------------------------------------ #
    # 값 로드 — GV{cell} 명령, 응답 2줄(상태코드 + 값)
    # ------------------------------------------------------------------ #

    def _load_values(self):
        # D3: 읽기 전용 표시
        exp = self._gv("D3")
        print(f"[설정창] GVD3 → {exp!r}")
        if exp is not None:
            self._exposure_label.setText(f"{exp:.3f} ms")

        for _, cell, kind, _, _, _, _ in _PARAMS:
            val = self._gv(cell)
            print(f"[설정창] GV{cell} → {val!r}")
            w = self._widgets[cell]
            if val is not None:
                if kind == "double":
                    w.setValue(val)
                else:
                    w.setValue(int(round(val)))
                self._originals[cell] = val
            else:
                self._originals[cell] = None

    def _gv(self, cell: str):
        """GV{cell} 전송 → 숫자 반환, 실패 시 None"""
        try:
            self._cam._send(f"GV{cell}")
            status = self._cam._read_line()   # "1" 또는 "0"
            if status != "1":
                print(f"[설정창] GV{cell} 상태코드: {status!r}")
                return None
            raw = self._cam._read_line()       # 실제 값
            return float(raw)
        except Exception as e:
            print(f"[설정창] GV{cell} 오류: {e}")
            return None

    # ------------------------------------------------------------------ #
    # 확인 버튼 — SV{cell} {value} 명령
    # ------------------------------------------------------------------ #

    def _on_ok(self):
        import time

        # 변경 항목 수집
        changes = []
        for _, cell, kind, _, _, _, _ in _PARAMS:
            w = self._widgets[cell]
            current  = float(w.value())
            original = self._originals.get(cell)
            if original is None or abs(current - original) >= 1e-9:
                changes.append((cell, current, kind))

        if not changes:
            self.accept()
            return

        # 오프라인 전환
        self._cam._send("SO0")
        resp = self._cam._read_line()
        print(f"[설정창] SO0 (오프라인) → {resp!r}")
        if resp.strip() != "1":
            QMessageBox.critical(self, "오류", f"오프라인 전환 실패 (응답: {resp!r})")
            return

        time.sleep(0.3)  # 카메라가 오프라인 전환 완료 대기

        # 값 설정
        errors = []
        for cell, value, kind in changes:
            if not self._sv(cell, value, kind):
                errors.append(cell)

        time.sleep(0.2)  # SV 처리 완료 대기

        # 온라인 복귀 — 최대 3회 재시도
        online_ok = False
        for attempt in range(3):
            self._cam._send("SO1")
            resp = self._cam._read_line()
            print(f"[설정창] SO1 (온라인) 시도 {attempt+1} → {resp!r}")
            if resp.strip() == "1":
                online_ok = True
                break
            time.sleep(0.5)

        if not online_ok:
            QMessageBox.warning(
                self, "온라인 복귀 실패",
                f"SO1 명령이 실패했습니다 (마지막 응답: {resp!r})\n"
                "카메라를 수동으로 재시작하거나 In-Sight Explorer에서 Online으로 전환하세요."
            )

        if errors:
            QMessageBox.critical(
                self, "설정 실패",
                f"다음 셀 설정 실패:\n{chr(10).join(errors)}\n\n"
                "셀이 읽기 전용이거나 값 범위를 벗어났을 수 있습니다."
            )
            return

        self.accept()

    def _sv(self, cell: str, value: float, kind: str) -> bool:
        """SV{cell} {value} 전송 → 응답 '1'이면 True"""
        try:
            formatted = str(int(round(value))) if kind == "int" else f"{value:.6g}"
            self._cam._send(f"SV{cell} {formatted}")
            resp = self._cam._read_line()
            print(f"[설정창] SV{cell} {formatted} → {resp!r}")
            return resp.strip() == "1"
        except Exception as e:
            print(f"[설정창] SV{cell} 오류: {e}")
            return False
