# 이미지 설정 다이얼로그 — In-Sight 2000C Telnet 셀 직접 GV/SV
from PyQt5.QtWidgets import (
    QDialog, QFormLayout, QVBoxLayout, QHBoxLayout,
    QPushButton, QDoubleSpinBox, QSpinBox, QMessageBox, QLabel,
)
from PyQt5.QtCore import Qt


# (레이블, 셀주소, 위젯종류, min, max, 기본값, 소수점자리)
_PARAMS = [
    ("노출 (밀리초)",      "D3",  "double", 0.01, 1000.0,  30.0, 3),
    ("최대 노출 시간",     "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),
]

# 스캔할 셀 범위 (A1~J20)
_SCAN_COLS = list("ABCDEFGHIJ")
_SCAN_ROWS = range(1, 21)

_STYLE = """
QDialog, QWidget {
    background: #1a1a1a;
    color: #ffffff;
    font-size: 14px;
}
QLabel { color: #cccccc; }
QDoubleSpinBox, QSpinBox {
    background: #2a2a2a;
    color: #ffffff;
    border: 1px solid #555555;
    border-radius: 4px;
    padding: 4px 8px;
    min-height: 38px;
}
QDoubleSpinBox::up-button, QSpinBox::up-button {
    subcontrol-origin: border;
    subcontrol-position: top right;
    width: 30px;
}
QDoubleSpinBox::down-button, QSpinBox::down-button {
    subcontrol-origin: border;
    subcontrol-position: bottom right;
    width: 30px;
}
QPushButton {
    background: #2e2e2e;
    color: #ffffff;
    border: 1px solid #555555;
    border-radius: 4px;
    min-height: 56px;
    padding: 0 24px;
    font-size: 15px;
}
QPushButton:hover  { background: #3a3a3a; }
QPushButton:pressed { background: #1e1e1e; }
QPushButton#scan {
    min-height: 38px;
    font-size: 13px;
    color: #aaaaaa;
}
"""


class ImageSettingsDialog(QDialog):
    def __init__(self, insight_cam, parent=None):
        super().__init__(parent)
        self._cam       = insight_cam
        self._widgets   = {}   # 셀주소 → 위젯
        self._originals = {}   # 셀주소 → 로드 시 원본값

        self.setWindowTitle("이미지 설정 — In-Sight 2000C")
        self.setMinimumWidth(440)
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        self.setStyleSheet(_STYLE)

        self._build_ui()
        self._load_values()

    # ================================================================== #
    # UI 구성
    # ================================================================== #

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

        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 if decimals > 0 else 1.0)
            else:
                w = QSpinBox()
                w.setRange(int(lo), int(hi))
                w.setValue(int(default))

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

        btn_scan = QPushButton("쓰기 가능한 셀 자동 탐지…")
        btn_scan.setObjectName("scan")
        btn_scan.clicked.connect(self._on_scan)

        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.addWidget(btn_scan)
        btn_row.addStretch()
        btn_row.addWidget(btn_ok)
        btn_row.addWidget(btn_cancel)

        root = QVBoxLayout(self)
        root.setContentsMargins(24, 20, 24, 20)
        root.setSpacing(16)
        root.addLayout(form)
        root.addLayout(btn_row)

    # ================================================================== #
    # 값 로드 — GV{cell}: 응답 "1" + 값
    # ================================================================== #

    def _load_values(self):
        for _, cell, kind, _, _, _, _ in _PARAMS:
            val = self._gv(cell)
            print(f"[설정창] GV{cell} → {val!r}")
            w = self._widgets[cell]
            if val is not None:
                w.setValue(val if kind == "double" else int(round(val)))
                self._originals[cell] = val
            else:
                self._originals[cell] = None   # 조회 실패 → 기본값 유지

    def _gv(self, cell: str):
        """GV{cell} 전송 → float 반환, 실패 시 None"""
        try:
            self._cam._send(f"GV{cell}")
            status = self._cam._read_line()
            if status != "1":
                return None
            return float(self._cam._read_line())
        except Exception:
            return None

    # ================================================================== #
    # 셀 자동 탐지 — GV로 읽히고 SV로 쓸 수 있는 셀 목록
    # ================================================================== #

    def _on_scan(self):
        """A1~J20 범위를 스캔해 Online(쓰기 가능) 셀을 찾아 표시."""
        readable  = []   # [(cell, value)]
        writable  = []   # [(cell, value)]

        for col in _SCAN_COLS:
            for row in _SCAN_ROWS:
                cell = f"{col}{row}"
                val = self._gv(cell)
                if val is None:
                    continue
                readable.append((cell, val))
                ok, _ = self._sv(cell, val, "double")   # 같은 값 재기입 → 실질적 변경 없음
                if ok:
                    writable.append((cell, val))

        if writable:
            lines = "\n".join(f"  {c}  =  {v}" for c, v in writable)
            msg = (
                f"쓰기 가능한 셀 {len(writable)}개 발견:\n\n"
                f"{lines}\n\n"
                f"image_settings_dialog.py 상단 _PARAMS의\n"
                f"셀 주소를 위 주소로 변경하세요."
            )
        elif readable:
            lines = "\n".join(f"  {c}  =  {v}" for c, v in readable[:20])
            msg = (
                f"읽기 전용 셀 {len(readable)}개 발견 (쓰기 가능 셀 없음):\n\n"
                f"{lines}\n\n"
                f"─────────────────────────────\n"
                f"Cognex In-Sight Explorer에서\n"
                f"제어하려는 셀을 우클릭 →\n"
                f"[Cell Properties] → [Online] 체크 후\n"
                f"job을 카메라에 업로드하세요.\n\n"
                f"이후 이 버튼을 다시 누르면\n"
                f"쓰기 가능한 셀 주소를 알 수 있습니다."
            )
        else:
            msg = (
                f"A1~J20 범위에서 읽을 수 있는 셀이 없습니다.\n\n"
                f"카메라 연결 상태 또는 job 파일을 확인하세요."
            )

        QMessageBox.information(self, "셀 탐지 결과", msg)

    # ================================================================== #
    # 확인 버튼 — 변경된 항목만 SV{cell} {value}
    # ================================================================== #

    def _on_ok(self):
        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

        errors = []
        for cell, value, kind in changes:
            ok, resp = self._sv(cell, value, kind)
            if not ok:
                errors.append((cell, resp))

        if errors:
            detail = "\n".join(f"  • {c}  (카메라 응답: {r!r})" for c, r in errors)
            QMessageBox.critical(
                self, "설정 실패",
                f"다음 셀 설정에 실패했습니다:\n{detail}\n\n"
                f"'쓰기 가능한 셀 자동 탐지' 버튼으로\n"
                f"현재 Online 셀 주소를 확인하세요.",
            )
            return

        self.accept()

    def _sv(self, cell: str, value: float, kind: str):
        """SV{cell} {value} 전송 → (성공여부, 응답코드 문자열)"""
        try:
            fmt = str(int(round(value))) if kind == "int" else f"{value:.6g}"
            self._cam._send(f"SV{cell} {fmt}")
            resp = self._cam._read_line()
            print(f"[설정창] SV{cell} {fmt} → {resp!r}")
            return resp.strip() == "1", resp.strip()
        except Exception as e:
            print(f"[설정창] SV{cell} 오류: {e}")
            return False, str(e)
