"""
PQ 单步调试面板
支持屏模组、SDR、HDR 三种测试类型的单步调试功能
"""

import copy

import ttkbootstrap as ttk
import tkinter as tk
from tkinter import messagebox
import threading
import time


def _theme_colors():
    style = ttk.Style()
    colors = style.colors
    return {
        "fg": colors.fg,
        "muted": colors.secondary,
        "info": colors.info,
        "warning": colors.warning,
        "error": colors.danger,
    }


class PQDebugPanel:
    """PQ 单步调试面板 - 支持 Gamma/EOTF/色准单步测试"""

    def __init__(self, parent, app):
        """
        初始化单步调试面板

        Args:
            parent: 父容器
            app: 主应用程序实例（用于访问设备和配置）
        """
        self.parent = parent
        self.app = app

        # 当前测试类型和项目
        self.current_test_type = None  # screen_module / sdr_movie / hdr_movie
        self.current_test_item = None  # gamma / eotf / accuracy

        # 测试完成标志
        self.test_completed = False

        # 原始测试数据（用于对比）
        self.original_data = {}

        # ==================== 创建主容器并自动 pack ====================
        self.main_container = ttk.Frame(parent)
        self.main_container.pack(fill=tk.BOTH, expand=True)

        # 创建 LabelFrame 放在 main_container 中
        self.main_frame = ttk.LabelFrame(
            self.main_container, text="单步调试", padding=10
        )
        self.main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # 创建三个独立的调试面板
        self.create_screen_module_panel()
        self.create_sdr_panel()
        self.create_hdr_panel()

        # 默认隐藏所有面板
        self.hide_all_panels()

    # ==================== 创建三个独立面板 ====================

    def create_screen_module_panel(self):
        """创建屏模组单步调试面板"""
        self.screen_frame = ttk.LabelFrame(
            self.main_frame, text="屏模组 - 单步调试", padding=10
        )

        # Notebook 用于切换 Gamma 和 RGB 调试
        self.screen_notebook = ttk.Notebook(self.screen_frame)
        self.screen_notebook.pack(fill=tk.BOTH, expand=True)

        # ========== Gamma 调试页 ==========
        self.screen_gamma_frame = ttk.Frame(self.screen_notebook, padding=10)
        self.screen_notebook.add(self.screen_gamma_frame, text="Gamma 调试")

        ttk.Label(
            self.screen_gamma_frame,
            text="测试完成后可用，选择灰阶进行单步调试",
            font=("SimHei", 9),
            foreground=_theme_colors()["muted"],
        ).grid(row=0, column=0, columnspan=3, pady=(0, 10))

        # 灰阶选择
        ttk.Label(self.screen_gamma_frame, text="选择灰阶:").grid(
            row=1, column=0, sticky=tk.W, padx=5, pady=5
        )

        self.screen_gray_var = tk.StringVar(value="0%")
        self.screen_gray_combo = ttk.Combobox(
            self.screen_gamma_frame,
            textvariable=self.screen_gray_var,
            values=[f"{i}%" for i in [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]],
            state="disabled",
            width=12,
        )
        self.screen_gray_combo.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5)

        # 测试按钮
        self.screen_test_btn = ttk.Button(
            self.screen_gamma_frame,
            text="单步测试",
            command=lambda: self.run_single_step("screen_module", "gamma"),
            state=tk.DISABLED,
            bootstyle="success",
        )
        self.screen_test_btn.grid(row=1, column=2, sticky=tk.W, padx=5, pady=5)

        # 对比结果显示区域
        self.screen_result_frame = ttk.Frame(self.screen_gamma_frame)
        self.screen_result_frame.grid(
            row=2, column=0, columnspan=3, sticky="nsew", pady=10
        )

        # 创建对比表格
        columns = ("项目", "原始测试", "单步测试", "差异")
        self.screen_result_tree = ttk.Treeview(
            self.screen_result_frame,
            columns=columns,
            show="headings",
            height=6,
        )

        for col in columns:
            self.screen_result_tree.heading(col, text=col)
            self.screen_result_tree.column(col, width=120, anchor=tk.CENTER)

        self.screen_result_tree.pack(fill=tk.BOTH, expand=True)

        scrollbar = ttk.Scrollbar(
            self.screen_result_frame,
            orient=tk.VERTICAL,
            command=self.screen_result_tree.yview,
        )
        self.screen_result_tree.configure(yscrollcommand=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # ========== RGB 调试页 ==========
        self.screen_rgb_frame = ttk.Frame(self.screen_notebook, padding=10)
        self.screen_notebook.add(self.screen_rgb_frame, text="RGB 调试")

        ttk.Label(
            self.screen_rgb_frame,
            text="测试完成后可用，选择颜色进行单步调试",
            font=("SimHei", 9),
            foreground=_theme_colors()["muted"],
        ).grid(row=0, column=0, columnspan=3, pady=(0, 10))

        # RGB 颜色选择
        ttk.Label(self.screen_rgb_frame, text="选择颜色:").grid(
            row=1, column=0, sticky=tk.W, padx=5, pady=5
        )

        self.screen_rgb_var = tk.StringVar(value="Red")
        self.screen_rgb_combo = ttk.Combobox(
            self.screen_rgb_frame,
            textvariable=self.screen_rgb_var,
            values=["Red", "Green", "Blue"],
            state="disabled",
            width=12,
        )
        self.screen_rgb_combo.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5)

        self.screen_rgb_test_btn = ttk.Button(
            self.screen_rgb_frame,
            text="单步测试",
            command=lambda: self.run_single_step("screen_module", "rgb"),
            state=tk.DISABLED,
            bootstyle="info",
        )
        self.screen_rgb_test_btn.grid(row=1, column=2, sticky=tk.W, padx=5, pady=5)

        # RGB 对比结果
        self.screen_rgb_result_frame = ttk.Frame(self.screen_rgb_frame)
        self.screen_rgb_result_frame.grid(
            row=2, column=0, columnspan=3, sticky="nsew", pady=10
        )

        columns = ("项目", "原始测试", "单步测试", "差异")
        self.screen_rgb_result_tree = ttk.Treeview(
            self.screen_rgb_result_frame,
            columns=columns,
            show="headings",
            height=6,
        )

        for col in columns:
            self.screen_rgb_result_tree.heading(col, text=col)
            self.screen_rgb_result_tree.column(col, width=120, anchor=tk.CENTER)

        self.screen_rgb_result_tree.pack(fill=tk.BOTH, expand=True)

        scrollbar = ttk.Scrollbar(
            self.screen_rgb_result_frame,
            orient=tk.VERTICAL,
            command=self.screen_rgb_result_tree.yview,
        )
        self.screen_rgb_result_tree.configure(yscrollcommand=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

    def create_sdr_panel(self):
        """创建 SDR 单步调试面板"""
        self.sdr_frame = ttk.LabelFrame(
            self.main_frame, text="SDR Movie - 单步调试", padding=10
        )

        # Notebook 用于切换 Gamma 和色准
        self.sdr_notebook = ttk.Notebook(self.sdr_frame)
        self.sdr_notebook.pack(fill=tk.BOTH, expand=True)

        # ========== Gamma 调试页 ==========
        self.sdr_gamma_frame = ttk.Frame(self.sdr_notebook, padding=10)
        self.sdr_notebook.add(self.sdr_gamma_frame, text="Gamma 调试")

        ttk.Label(
            self.sdr_gamma_frame,
            text="测试完成后可用，选择灰阶进行单步调试",
            font=("SimHei", 9),
            foreground=_theme_colors()["muted"],
        ).grid(row=0, column=0, columnspan=3, pady=(0, 10))

        ttk.Label(self.sdr_gamma_frame, text="选择灰阶:").grid(
            row=1, column=0, sticky=tk.W, padx=5, pady=5
        )

        self.sdr_gray_var = tk.StringVar(value="0%")
        self.sdr_gray_combo = ttk.Combobox(
            self.sdr_gamma_frame,
            textvariable=self.sdr_gray_var,
            values=[f"{i}%" for i in [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]],
            state="disabled",
            width=12,
        )
        self.sdr_gray_combo.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5)

        self.sdr_gamma_test_btn = ttk.Button(
            self.sdr_gamma_frame,
            text="单步测试",
            command=lambda: self.run_single_step("sdr_movie", "gamma"),
            state=tk.DISABLED,
            bootstyle="success",
        )
        self.sdr_gamma_test_btn.grid(row=1, column=2, sticky=tk.W, padx=5, pady=5)

        # 对比结果
        self.sdr_gamma_result_frame = ttk.Frame(self.sdr_gamma_frame)
        self.sdr_gamma_result_frame.grid(
            row=2, column=0, columnspan=3, sticky="nsew", pady=10
        )

        columns = ("项目", "原始测试", "单步测试", "差异")
        self.sdr_gamma_result_tree = ttk.Treeview(
            self.sdr_gamma_result_frame,
            columns=columns,
            show="headings",
            height=6,
        )

        for col in columns:
            self.sdr_gamma_result_tree.heading(col, text=col)
            self.sdr_gamma_result_tree.column(col, width=120, anchor=tk.CENTER)

        self.sdr_gamma_result_tree.pack(fill=tk.BOTH, expand=True)

        scrollbar = ttk.Scrollbar(
            self.sdr_gamma_result_frame,
            orient=tk.VERTICAL,
            command=self.sdr_gamma_result_tree.yview,
        )
        self.sdr_gamma_result_tree.configure(yscrollcommand=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # ========== 色准调试页 ==========
        self.sdr_accuracy_frame = ttk.Frame(self.sdr_notebook, padding=10)
        self.sdr_notebook.add(self.sdr_accuracy_frame, text="色准调试")

        ttk.Label(
            self.sdr_accuracy_frame,
            text="测试完成后可用，选择色块进行单步调试",
            font=("SimHei", 9),
            foreground=_theme_colors()["muted"],
        ).grid(row=0, column=0, columnspan=3, pady=(0, 10))

        ttk.Label(self.sdr_accuracy_frame, text="选择色块:").grid(
            row=1, column=0, sticky=tk.W, padx=5, pady=5
        )

        self.sdr_color_var = tk.StringVar(value="White")
        self.sdr_color_combo = ttk.Combobox(
            self.sdr_accuracy_frame,
            textvariable=self.sdr_color_var,
            values=self.get_29_color_names(),
            state="disabled",
            width=20,
        )
        self.sdr_color_combo.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5)

        self.sdr_accuracy_test_btn = ttk.Button(
            self.sdr_accuracy_frame,
            text="单步测试",
            command=lambda: self.run_single_step("sdr_movie", "accuracy"),
            state=tk.DISABLED,
            bootstyle="warning",
        )
        self.sdr_accuracy_test_btn.grid(row=1, column=2, sticky=tk.W, padx=5, pady=5)

        # 对比结果
        self.sdr_accuracy_result_frame = ttk.Frame(self.sdr_accuracy_frame)
        self.sdr_accuracy_result_frame.grid(
            row=2, column=0, columnspan=3, sticky="nsew", pady=10
        )

        columns = ("项目", "原始测试", "单步测试", "差异")
        self.sdr_accuracy_result_tree = ttk.Treeview(
            self.sdr_accuracy_result_frame,
            columns=columns,
            show="headings",
            height=6,
        )

        for col in columns:
            self.sdr_accuracy_result_tree.heading(col, text=col)
            self.sdr_accuracy_result_tree.column(col, width=120, anchor=tk.CENTER)

        self.sdr_accuracy_result_tree.pack(fill=tk.BOTH, expand=True)

        scrollbar = ttk.Scrollbar(
            self.sdr_accuracy_result_frame,
            orient=tk.VERTICAL,
            command=self.sdr_accuracy_result_tree.yview,
        )
        self.sdr_accuracy_result_tree.configure(yscrollcommand=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # ========== RGB 调试页 ==========
        self.sdr_rgb_frame = ttk.Frame(self.sdr_notebook, padding=10)
        self.sdr_notebook.add(self.sdr_rgb_frame, text="RGB 调试")

        ttk.Label(
            self.sdr_rgb_frame,
            text="测试完成后可用，选择颜色进行单步调试",
            font=("SimHei", 9),
            foreground=_theme_colors()["muted"],
        ).grid(row=0, column=0, columnspan=3, pady=(0, 10))

        # RGB 颜色选择
        ttk.Label(self.sdr_rgb_frame, text="选择颜色:").grid(
            row=1, column=0, sticky=tk.W, padx=5, pady=5
        )

        self.sdr_rgb_var = tk.StringVar(value="Red")
        self.sdr_rgb_combo = ttk.Combobox(
            self.sdr_rgb_frame,
            textvariable=self.sdr_rgb_var,
            values=["Red", "Green", "Blue"],
            state="disabled",
            width=12,
        )
        self.sdr_rgb_combo.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5)

        self.sdr_rgb_test_btn = ttk.Button(
            self.sdr_rgb_frame,
            text="单步测试",
            command=lambda: self.run_single_step("sdr_movie", "rgb"),
            state=tk.DISABLED,
            bootstyle="info",
        )
        self.sdr_rgb_test_btn.grid(row=1, column=2, sticky=tk.W, padx=5, pady=5)

        # RGB 对比结果
        self.sdr_rgb_result_frame = ttk.Frame(self.sdr_rgb_frame)
        self.sdr_rgb_result_frame.grid(
            row=2, column=0, columnspan=3, sticky="nsew", pady=10
        )

        columns = ("项目", "原始测试", "单步测试", "差异")
        self.sdr_rgb_result_tree = ttk.Treeview(
            self.sdr_rgb_result_frame,
            columns=columns,
            show="headings",
            height=6,
        )

        for col in columns:
            self.sdr_rgb_result_tree.heading(col, text=col)
            self.sdr_rgb_result_tree.column(col, width=120, anchor=tk.CENTER)

        self.sdr_rgb_result_tree.pack(fill=tk.BOTH, expand=True)

        scrollbar = ttk.Scrollbar(
            self.sdr_rgb_result_frame,
            orient=tk.VERTICAL,
            command=self.sdr_rgb_result_tree.yview,
        )
        self.sdr_rgb_result_tree.configure(yscrollcommand=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

    def create_hdr_panel(self):
        """创建 HDR 单步调试面板"""
        self.hdr_frame = ttk.LabelFrame(
            self.main_frame, text="HDR Movie - 单步调试", padding=10
        )

        # Notebook 用于切换 EOTF 和色准
        self.hdr_notebook = ttk.Notebook(self.hdr_frame)
        self.hdr_notebook.pack(fill=tk.BOTH, expand=True)

        # ========== EOTF 调试页 ==========
        self.hdr_eotf_frame = ttk.Frame(self.hdr_notebook, padding=10)
        self.hdr_notebook.add(self.hdr_eotf_frame, text="EOTF 调试")

        ttk.Label(
            self.hdr_eotf_frame,
            text="测试完成后可用，选择灰阶进行单步调试",
            font=("SimHei", 9),
            foreground=_theme_colors()["muted"],
        ).grid(row=0, column=0, columnspan=3, pady=(0, 10))

        ttk.Label(self.hdr_eotf_frame, text="选择灰阶:").grid(
            row=1, column=0, sticky=tk.W, padx=5, pady=5
        )

        self.hdr_gray_var = tk.StringVar(value="0%")
        self.hdr_gray_combo = ttk.Combobox(
            self.hdr_eotf_frame,
            textvariable=self.hdr_gray_var,
            values=[f"{i}%" for i in [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]],
            state="disabled",
            width=12,
        )
        self.hdr_gray_combo.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5)

        self.hdr_eotf_test_btn = ttk.Button(
            self.hdr_eotf_frame,
            text="单步测试",
            command=lambda: self.run_single_step("hdr_movie", "eotf"),
            state=tk.DISABLED,
            bootstyle="success",
        )
        self.hdr_eotf_test_btn.grid(row=1, column=2, sticky=tk.W, padx=5, pady=5)

        # 对比结果
        self.hdr_eotf_result_frame = ttk.Frame(self.hdr_eotf_frame)
        self.hdr_eotf_result_frame.grid(
            row=2, column=0, columnspan=3, sticky="nsew", pady=10
        )

        columns = ("项目", "原始测试", "单步测试", "差异")
        self.hdr_eotf_result_tree = ttk.Treeview(
            self.hdr_eotf_result_frame,
            columns=columns,
            show="headings",
            height=6,
        )

        for col in columns:
            self.hdr_eotf_result_tree.heading(col, text=col)
            self.hdr_eotf_result_tree.column(col, width=120, anchor=tk.CENTER)

        self.hdr_eotf_result_tree.pack(fill=tk.BOTH, expand=True)

        scrollbar = ttk.Scrollbar(
            self.hdr_eotf_result_frame,
            orient=tk.VERTICAL,
            command=self.hdr_eotf_result_tree.yview,
        )
        self.hdr_eotf_result_tree.configure(yscrollcommand=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # ========== 色准调试页 ==========
        self.hdr_accuracy_frame = ttk.Frame(self.hdr_notebook, padding=10)
        self.hdr_notebook.add(self.hdr_accuracy_frame, text="色准调试")

        ttk.Label(
            self.hdr_accuracy_frame,
            text="测试完成后可用，选择色块进行单步调试",
            font=("SimHei", 9),
            foreground=_theme_colors()["muted"],
        ).grid(row=0, column=0, columnspan=3, pady=(0, 10))

        ttk.Label(self.hdr_accuracy_frame, text="选择色块:").grid(
            row=1, column=0, sticky=tk.W, padx=5, pady=5
        )

        self.hdr_color_var = tk.StringVar(value="White")
        self.hdr_color_combo = ttk.Combobox(
            self.hdr_accuracy_frame,
            textvariable=self.hdr_color_var,
            values=self.get_29_color_names(),
            state="disabled",
            width=20,
        )
        self.hdr_color_combo.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5)

        self.hdr_accuracy_test_btn = ttk.Button(
            self.hdr_accuracy_frame,
            text="单步测试",
            command=lambda: self.run_single_step("hdr_movie", "accuracy"),
            state=tk.DISABLED,
            bootstyle="warning",
        )
        self.hdr_accuracy_test_btn.grid(row=1, column=2, sticky=tk.W, padx=5, pady=5)

        # 对比结果
        self.hdr_accuracy_result_frame = ttk.Frame(self.hdr_accuracy_frame)
        self.hdr_accuracy_result_frame.grid(
            row=2, column=0, columnspan=3, sticky="nsew", pady=10
        )

        columns = ("项目", "原始测试", "单步测试", "差异")
        self.hdr_accuracy_result_tree = ttk.Treeview(
            self.hdr_accuracy_result_frame,
            columns=columns,
            show="headings",
            height=6,
        )

        for col in columns:
            self.hdr_accuracy_result_tree.heading(col, text=col)
            self.hdr_accuracy_result_tree.column(col, width=120, anchor=tk.CENTER)

        self.hdr_accuracy_result_tree.pack(fill=tk.BOTH, expand=True)

        scrollbar = ttk.Scrollbar(
            self.hdr_accuracy_result_frame,
            orient=tk.VERTICAL,
            command=self.hdr_accuracy_result_tree.yview,
        )
        self.hdr_accuracy_result_tree.configure(yscrollcommand=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # ========== RGB 调试页 ==========
        self.hdr_rgb_frame = ttk.Frame(self.hdr_notebook, padding=10)
        self.hdr_notebook.add(self.hdr_rgb_frame, text="RGB 调试")

        ttk.Label(
            self.hdr_rgb_frame,
            text="测试完成后可用，选择颜色进行单步调试",
            font=("SimHei", 9),
            foreground=_theme_colors()["muted"],
        ).grid(row=0, column=0, columnspan=3, pady=(0, 10))

        # RGB 颜色选择
        ttk.Label(self.hdr_rgb_frame, text="选择颜色:").grid(
            row=1, column=0, sticky=tk.W, padx=5, pady=5
        )

        self.hdr_rgb_var = tk.StringVar(value="Red")
        self.hdr_rgb_combo = ttk.Combobox(
            self.hdr_rgb_frame,
            textvariable=self.hdr_rgb_var,
            values=["Red", "Green", "Blue"],
            state="disabled",
            width=12,
        )
        self.hdr_rgb_combo.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5)

        self.hdr_rgb_test_btn = ttk.Button(
            self.hdr_rgb_frame,
            text="单步测试",
            command=lambda: self.run_single_step("hdr_movie", "rgb"),
            state=tk.DISABLED,
            bootstyle="info",
        )
        self.hdr_rgb_test_btn.grid(row=1, column=2, sticky=tk.W, padx=5, pady=5)

        # RGB 对比结果
        self.hdr_rgb_result_frame = ttk.Frame(self.hdr_rgb_frame)
        self.hdr_rgb_result_frame.grid(
            row=2, column=0, columnspan=3, sticky="nsew", pady=10
        )

        columns = ("项目", "原始测试", "单步测试", "差异")
        self.hdr_rgb_result_tree = ttk.Treeview(
            self.hdr_rgb_result_frame,
            columns=columns,
            show="headings",
            height=6,
        )

        for col in columns:
            self.hdr_rgb_result_tree.heading(col, text=col)
            self.hdr_rgb_result_tree.column(col, width=120, anchor=tk.CENTER)

        self.hdr_rgb_result_tree.pack(fill=tk.BOTH, expand=True)

        scrollbar = ttk.Scrollbar(
            self.hdr_rgb_result_frame,
            orient=tk.VERTICAL,
            command=self.hdr_rgb_result_tree.yview,
        )
        self.hdr_rgb_result_tree.configure(yscrollcommand=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

    # ==================== 面板显示控制 ====================

    def hide_all_panels(self):
        """隐藏所有面板"""
        self.screen_frame.pack_forget()
        self.sdr_frame.pack_forget()
        self.hdr_frame.pack_forget()

    def show_panel(self, test_type):
        """根据测试类型显示对应面板"""
        self.hide_all_panels()

        if test_type == "screen_module":
            self.screen_frame.pack(fill=tk.BOTH, expand=True)
        elif test_type == "sdr_movie":
            self.sdr_frame.pack(fill=tk.BOTH, expand=True)
        elif test_type == "hdr_movie":
            self.hdr_frame.pack(fill=tk.BOTH, expand=True)

    # ==================== 启用/禁用控制 ====================

    def enable_debug(self, test_type, test_item, original_data):
        """
        测试完成后启用单步调试

        Args:
            test_type: screen_module / sdr_movie / hdr_movie
            test_item: gamma / eotf / accuracy / rgb
        """
        self.current_test_type = test_type
        self.current_test_item = test_item
        self.test_completed = True
        self.original_data[f"{test_type}_{test_item}"] = original_data

        # 显示对应面板
        self.show_panel(test_type)

        # 启用对应控件
        if test_type == "screen_module":
            if test_item == "gamma":
                self.screen_gray_combo.config(state="readonly")
                self.screen_test_btn.config(state=tk.NORMAL)
            elif test_item == "rgb":
                self.screen_rgb_combo.config(state="readonly")
                self.screen_rgb_test_btn.config(state=tk.NORMAL)

        elif test_type == "sdr_movie":
            if test_item == "gamma":
                self.sdr_gray_combo.config(state="readonly")
                self.sdr_gamma_test_btn.config(state=tk.NORMAL)
            elif test_item == "accuracy":
                self.sdr_color_combo.config(state="readonly")
                self.sdr_accuracy_test_btn.config(state=tk.NORMAL)
            elif test_item == "rgb":
                self.sdr_rgb_combo.config(state="readonly")
                self.sdr_rgb_test_btn.config(state=tk.NORMAL)

        elif test_type == "hdr_movie":
            if test_item == "eotf":
                self.hdr_gray_combo.config(state="readonly")
                self.hdr_eotf_test_btn.config(state=tk.NORMAL)
            elif test_item == "accuracy":
                self.hdr_color_combo.config(state="readonly")
                self.hdr_accuracy_test_btn.config(state=tk.NORMAL)
            elif test_item == "rgb":
                self.hdr_rgb_combo.config(state="readonly")
                self.hdr_rgb_test_btn.config(state=tk.NORMAL)

    def disable_all_debug(self):
        """禁用所有单步调试（新测试开始时调用）"""
        self.test_completed = False
        self.original_data.clear()

        # 禁用屏模组
        self.screen_gray_combo.config(state="disabled")
        self.screen_test_btn.config(state=tk.DISABLED)
        self.screen_result_tree.delete(*self.screen_result_tree.get_children())
        self.screen_rgb_combo.config(state="disabled")
        self.screen_rgb_test_btn.config(state=tk.DISABLED)
        self.screen_rgb_result_tree.delete(*self.screen_rgb_result_tree.get_children())

        # 禁用 SDR
        self.sdr_gray_combo.config(state="disabled")
        self.sdr_gamma_test_btn.config(state=tk.DISABLED)
        self.sdr_gamma_result_tree.delete(*self.sdr_gamma_result_tree.get_children())

        self.sdr_color_combo.config(state="disabled")
        self.sdr_accuracy_test_btn.config(state=tk.DISABLED)
        self.sdr_accuracy_result_tree.delete(
            *self.sdr_accuracy_result_tree.get_children()
        )

        self.sdr_rgb_combo.config(state="disabled")
        self.sdr_rgb_test_btn.config(state=tk.DISABLED)
        self.sdr_rgb_result_tree.delete(*self.sdr_rgb_result_tree.get_children())

        # 禁用 HDR
        self.hdr_gray_combo.config(state="disabled")
        self.hdr_eotf_test_btn.config(state=tk.DISABLED)
        self.hdr_eotf_result_tree.delete(*self.hdr_eotf_result_tree.get_children())

        self.hdr_color_combo.config(state="disabled")
        self.hdr_accuracy_test_btn.config(state=tk.DISABLED)
        self.hdr_accuracy_result_tree.delete(
            *self.hdr_accuracy_result_tree.get_children()
        )

        self.hdr_rgb_combo.config(state="disabled")
        self.hdr_rgb_test_btn.config(state=tk.DISABLED)
        self.hdr_rgb_result_tree.delete(*self.hdr_rgb_result_tree.get_children())

        self.hide_all_panels()

    # ==================== 单步测试执行 ====================

    def run_single_step(self, test_type, test_item):
        """
        执行单步测试

        Args:
            test_type: screen_module / sdr_movie / hdr_movie
            test_item: gamma / eotf / accuracy / rgb
        """
        # 检查设备连接
        if not self.app.ca or not self.app.is_ucd_connected:
            messagebox.showerror("错误", "请先连接设备")
            return

        # 获取选择的项目
        if test_item in ["gamma", "eotf"]:
            if test_type == "screen_module":
                selected = self.screen_gray_var.get()
            elif test_type == "sdr_movie":
                selected = self.sdr_gray_var.get()
            elif test_type == "hdr_movie":
                selected = self.hdr_gray_var.get()

        elif test_item == "accuracy":
            if test_type == "sdr_movie":
                selected = self.sdr_color_var.get()
            elif test_type == "hdr_movie":
                selected = self.hdr_color_var.get()

        elif test_item == "rgb":
            if test_type == "screen_module":
                selected = self.screen_rgb_var.get()
            elif test_type == "sdr_movie":
                selected = self.sdr_rgb_var.get()
            elif test_type == "hdr_movie":
                selected = self.hdr_rgb_var.get()

        # 在新线程中执行测试
        thread = threading.Thread(
            target=self._run_single_step_thread,
            args=(test_type, test_item, selected),
            daemon=True,
        )
        thread.start()

    def _run_single_step_thread(self, test_type, test_item, selected):
        """单步测试线程"""
        try:
            self.app.log_gui.log("=" * 50, level="info")
            self.app.log_gui.log(
                f"开始单步调试: {test_type} - {test_item} - {selected}"
            , level="info")
            self.app.log_gui.log("=" * 50, level="info")

            # 禁用按钮
            self._disable_test_button(test_type, test_item)

            # 获取图案索引并发送
            if test_item in ["gamma", "eotf"]:
                pattern_index = self.get_gray_index(selected)
                pattern_mode = "gray"
            elif test_item == "accuracy":
                pattern_index = self.get_color_index(selected)
                pattern_mode = "accuracy"
            elif test_item == "rgb":
                pattern_index = self.get_rgb_index(selected)
                pattern_mode = "rgb"
            else:
                raise ValueError(f"不支持的测试项目: {test_item}")

            session = self.app.pattern_service.prepare_session(
                pattern_mode,
                test_type=test_type,
                log_details=False,
            )
            self.app.pattern_service.send_session_pattern(session, pattern_index)
            time.sleep(1.5)

            # 测量数据
            x, y, lv, X, Y, Z = self.app.read_ca_xyLv()

            self.app.log_gui.log(
                f"测量完成: x={x:.4f}, y={y:.4f}, lv={lv:.2f}, "
                f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}"
            , level="success")

            # 对比数据
            self._compare_and_display(
                test_type, test_item, selected, [x, y, lv, X, Y, Z]
            )

            # 重新启用按钮
            self._enable_test_button(test_type, test_item)

        except Exception as e:
            self.app.log_gui.log(f"单步测试失败: {str(e)}", level="error")
            import traceback

            self.app.log_gui.log(traceback.format_exc(), level="error")
            self._enable_test_button(test_type, test_item)

    def _compare_and_display(self, test_type, test_item, selected, new_data):
        """对比数据并显示"""
        # 获取原始数据
        key = f"{test_type}_{test_item}"
        if key not in self.original_data:
            self.app.log_gui.log("未找到原始测试数据", level="error")
            return

        original_data_list = self.original_data[key]

        # 找到对应的原始数据
        if test_item in ["gamma", "eotf"]:
            index = self.get_gray_index(selected)
        elif test_item == "accuracy":
            index = self.get_color_index(selected)
        elif test_item == "rgb":
            index = self.get_rgb_index(selected)

        if index >= len(original_data_list):
            self.app.log_gui.log(f"索引超出范围: {index}", level="error")
            return

        original_data = original_data_list[index]

        # ==================== 构建对比数据 ====================
        comparison = {}

        if test_item in ("gamma", "eotf"):
            result_key = "gamma" if test_item == "gamma" else "eotf"
            original_gamma = self._get_gamma_from_results(result_key, index)
            new_gamma = self._calculate_gamma_at_index(
                test_type, test_item, index, new_data
            )
            comparison["x"] = (
                original_data[0],
                new_data[0],
                new_data[0] - original_data[0],
            )
            comparison["y"] = (
                original_data[1],
                new_data[1],
                new_data[1] - original_data[1],
            )
            comparison["lv"] = (
                original_data[2],
                new_data[2],
                new_data[2] - original_data[2],
            )
            comparison["γ"] = (
                original_gamma,
                new_gamma,
                new_gamma - original_gamma,
            )

        elif test_item == "accuracy":
            # ========== 色准测试：添加 ΔE 2000 值 ==========

            # 从原始测试结果中读取 ΔE 值
            original_delta_e = self._get_delta_e_from_results(test_type, selected)

            # 重新计算单步测试的 ΔE 值
            new_delta_e = self._calculate_delta_e_for_color(
                selected, new_data[0], new_data[1], new_data[2]
            )

            # ΔE 值放在最上面
            comparison["ΔE 2000"] = (
                original_delta_e,
                new_delta_e,
                new_delta_e - original_delta_e,
            )

            # 只保留 x, y, lv
            comparison["x"] = (
                original_data[0],
                new_data[0],
                new_data[0] - original_data[0],
            )
            comparison["y"] = (
                original_data[1],
                new_data[1],
                new_data[1] - original_data[1],
            )
            comparison["lv"] = (
                original_data[2],
                new_data[2],
                new_data[2] - original_data[2],
            )

        elif test_item == "rgb":
            # ========== RGB 测试：对比 x, y, lv ==========
            comparison["x"] = (
                original_data[0],
                new_data[0],
                new_data[0] - original_data[0],
            )
            comparison["y"] = (
                original_data[1],
                new_data[1],
                new_data[1] - original_data[1],
            )
            comparison["lv"] = (
                original_data[2],
                new_data[2],
                new_data[2] - original_data[2],
            )

        # 显示到对应的 Treeview
        self._display_comparison(test_type, test_item, selected, comparison)

    def _display_comparison(self, test_type, test_item, selected, comparison):
        """显示对比结果到 Treeview"""
        # 获取对应的 Treeview
        if test_type == "screen_module":
            if test_item == "gamma":
                tree = self.screen_result_tree
            elif test_item == "rgb":
                tree = self.screen_rgb_result_tree
        elif test_type == "sdr_movie":
            if test_item == "gamma":
                tree = self.sdr_gamma_result_tree
            elif test_item == "accuracy":
                tree = self.sdr_accuracy_result_tree
            elif test_item == "rgb":
                tree = self.sdr_rgb_result_tree
        elif test_type == "hdr_movie":
            if test_item == "eotf":
                tree = self.hdr_eotf_result_tree
            elif test_item == "accuracy":
                tree = self.hdr_accuracy_result_tree
            elif test_item == "rgb":
                tree = self.hdr_rgb_result_tree

        # 清空旧数据
        tree.delete(*tree.get_children())

        # 插入标题行
        tree.insert(
            "",
            "end",
            values=(f"【{selected}】", "", "", ""),
            tags=("header",),
        )

        # 插入对比数据
        for key, (original, new, diff) in comparison.items():
            # 根据参数类型设置格式和阈值
            if key == "ΔE 2000":
                # ΔE 使用 2 位小数
                original_str = f"{original:.2f}"
                new_str = f"{new:.2f}"
                diff_str = f"{diff:+.2f}"
                threshold = 0.5

            elif key == "lv":
                # 亮度使用 2 位小数
                original_str = f"{original:.2f}"
                new_str = f"{new:.2f}"
                diff_str = f"{diff:+.2f}"
                threshold = 0.5

            elif key == "γ":
                original_str = self._format_gamma_value(original)
                new_str = self._format_gamma_value(new)
                if original_str == "--" or new_str == "--":
                    diff_str = "--"
                    threshold = None
                else:
                    diff_str = f"{diff:+.2f}"
                    threshold = 0.05

            else:  # x, y
                # x, y 使用 4 位小数
                original_str = f"{original:.4f}"
                new_str = f"{new:.4f}"
                diff_str = f"{diff:+.4f}"
                threshold = 0.001

            # 判断差异
            if threshold is None:
                tag = "normal"
            elif abs(diff) > threshold:
                tag = "warning"
            else:
                tag = "normal"

            # ΔE 使用特殊标签
            if key == "ΔE 2000":
                tag = "highlight" if tag == "normal" else "highlight_warning"

            tree.insert(
                "",
                "end",
                values=(key, original_str, new_str, diff_str),
                tags=(tag,),
            )

        # 设置标签样式
        palette = _theme_colors()
        tree.tag_configure("header", background=palette["info"], font=("SimHei", 9, "bold"))
        tree.tag_configure("normal", foreground=palette["fg"])
        tree.tag_configure("warning", foreground=palette["warning"])
        tree.tag_configure("highlight", foreground=palette["info"], font=("SimHei", 9, "bold"))
        tree.tag_configure(
            "highlight_warning", foreground=palette["warning"], font=("SimHei", 9, "bold")
        )

    def _disable_test_button(self, test_type, test_item):
        """禁用测试按钮"""
        if test_type == "screen_module":
            if test_item == "gamma":
                self.screen_test_btn.config(state=tk.DISABLED)
            elif test_item == "rgb":
                self.screen_rgb_test_btn.config(state=tk.DISABLED)
        elif test_type == "sdr_movie":
            if test_item == "gamma":
                self.sdr_gamma_test_btn.config(state=tk.DISABLED)
            elif test_item == "accuracy":
                self.sdr_accuracy_test_btn.config(state=tk.DISABLED)
            elif test_item == "rgb":
                self.sdr_rgb_test_btn.config(state=tk.DISABLED)
        elif test_type == "hdr_movie":
            if test_item == "eotf":
                self.hdr_eotf_test_btn.config(state=tk.DISABLED)
            elif test_item == "accuracy":
                self.hdr_accuracy_test_btn.config(state=tk.DISABLED)
            elif test_item == "rgb":
                self.hdr_rgb_test_btn.config(state=tk.DISABLED)

    def _enable_test_button(self, test_type, test_item):
        """启用测试按钮"""
        dispatch = self.app._dispatch_ui
        if test_type == "screen_module":
            if test_item == "gamma":
                dispatch(self.screen_test_btn.config, state=tk.NORMAL)
            elif test_item == "rgb":
                dispatch(self.screen_rgb_test_btn.config, state=tk.NORMAL)
        elif test_type == "sdr_movie":
            if test_item == "gamma":
                dispatch(self.sdr_gamma_test_btn.config, state=tk.NORMAL)
            elif test_item == "accuracy":
                dispatch(self.sdr_accuracy_test_btn.config, state=tk.NORMAL)
            elif test_item == "rgb":
                dispatch(self.sdr_rgb_test_btn.config, state=tk.NORMAL)
        elif test_type == "hdr_movie":
            if test_item == "eotf":
                dispatch(self.hdr_eotf_test_btn.config, state=tk.NORMAL)
            elif test_item == "accuracy":
                dispatch(self.hdr_accuracy_test_btn.config, state=tk.NORMAL)
            elif test_item == "rgb":
                dispatch(self.hdr_rgb_test_btn.config, state=tk.NORMAL)

    # ==================== 辅助方法 ====================

    def get_gray_index(self, gray_str):
        """获取灰阶索引"""
        gray_levels = [100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0]
        gray_value = int(gray_str.replace("%", ""))
        return gray_levels.index(gray_value)

    def get_color_index(self, color_name):
        """获取色块索引"""
        color_names = self.get_29_color_names()
        return color_names.index(color_name)

    def get_rgb_index(self, color_name):
        """获取RGB颜色索引"""
        rgb_colors = ["Red", "Green", "Blue"]
        return rgb_colors.index(color_name)

    def get_29_color_names(self):
        """获取29色名称列表"""
        return [
            "White",
            "Gray 80",
            "Gray 65",
            "Gray 50",
            "Gray 35",
            "Dark Skin",
            "Light Skin",
            "Blue Sky",
            "Foliage",
            "Blue Flower",
            "Bluish Green",
            "Orange",
            "Purplish Blue",
            "Moderate Red",
            "Purple",
            "Yellow Green",
            "Orange Yellow",
            "Blue (Legacy)",
            "Green (Legacy)",
            "Red (Legacy)",
            "Yellow (Legacy)",
            "Magenta (Legacy)",
            "Cyan (Legacy)",
            "100% Red",
            "100% Green",
            "100% Blue",
            "100% Cyan",
            "100% Magenta",
            "100% Yellow",
        ]

    def _format_gamma_value(self, gamma):
        """格式化 Gamma 值，无效时显示 --"""
        if gamma is None or gamma < 0.5 or gamma > 5.0:
            return "--"
        return f"{gamma:.2f}"

    def _get_gamma_calc_params(self, results):
        """获取 Gamma 计算参数（与批量测试保持一致）"""
        config_max_value = self.app.config.current_pattern.get(
            "measurement_max_value", 10
        )
        try:
            max_index_fix = int(config_max_value)
        except (ValueError, TypeError):
            max_index_fix = 10

        actual_max_index = len(results) - 1
        if max_index_fix > actual_max_index:
            max_index_fix = actual_max_index

        pattern_params = self.app.config.default_pattern_gray.get(
            "pattern_params", None
        )
        return max_index_fix, pattern_params

    def _get_gamma_from_results(self, result_key, index):
        """从原始测试结果中读取指定灰阶的 Gamma 值"""
        try:
            if result_key not in self.app.results.test_items:
                return 0.0

            result_data = self.app.results.test_items[result_key].final_result
            if not result_data:
                return 0.0

            gamma_list = result_data.get(result_key, [])
            if index >= len(gamma_list):
                return 0.0

            item = gamma_list[index]
            return item[3] if len(item) > 3 else 0.0

        except Exception as e:
            self.app.log_gui.log(f"读取 Gamma 失败: {str(e)}", level="error")
            return 0.0

    def _calculate_gamma_at_index(self, test_type, test_item, index, new_data):
        """用单步测量更新对应灰阶后，重新计算该点的 Gamma 值"""
        try:
            key = f"{test_type}_{test_item}"
            results = copy.deepcopy(self.original_data[key])

            updated = list(results[index])
            updated[0] = new_data[0]
            updated[1] = new_data[1]
            updated[2] = new_data[2]
            for i in range(3, min(len(updated), len(new_data))):
                updated[i] = new_data[i]
            results[index] = updated

            max_index_fix, pattern_params = self._get_gamma_calc_params(results)
            results_with_gamma, _ = self.app.calculate_gamma(
                results, max_index_fix, pattern_params
            )

            if index >= len(results_with_gamma):
                return 0.0
            return results_with_gamma[index][3]

        except Exception as e:
            self.app.log_gui.log(f"计算 Gamma 失败: {str(e)}", level="error")
            return 0.0

    def _get_delta_e_from_results(self, test_type, color_name):
        """从原始测试结果中读取 ΔE 值"""
        try:
            self.app.log_gui.log(f"[读取 ΔE] 开始:", level="info")
            self.app.log_gui.log(f"  test_type = {test_type}", level="info")
            self.app.log_gui.log(f"  color_name = {color_name}", level="info")

            # 正确的访问方式：通过 test_items
            if "accuracy" not in self.app.results.test_items:
                self.app.log_gui.log("未找到 accuracy 测试项", level="error")
                return 0.0

            test_item = self.app.results.test_items["accuracy"]
            accuracy_result = test_item.final_result

            self.app.log_gui.log(f"  accuracy_result = {accuracy_result is not None}", level="info")

            if not accuracy_result:
                self.app.log_gui.log("accuracy_result 为空", level="error")
                return 0.0

            # 获取色块名称列表和 ΔE 值列表
            color_patches = accuracy_result.get("color_patches", [])
            delta_e_values = accuracy_result.get("delta_e_values", [])

            self.app.log_gui.log(f"  color_patches 数量: {len(color_patches)}", level="info")
            self.app.log_gui.log(f"  delta_e_values 数量: {len(delta_e_values)}", level="info")

            if color_patches:
                self.app.log_gui.log(f"  前3个色块: {color_patches[:3]}", level="info")
            if delta_e_values:
                self.app.log_gui.log(f"  前3个ΔE: {delta_e_values[:3]}", level="info")

            # 查找对应色块的索引
            try:
                index = color_patches.index(color_name)
                delta_e = delta_e_values[index]
                self.app.log_gui.log(
                    f"  找到 {color_name}: index={index}, ΔE={delta_e:.2f}"
                , level="success")
                return delta_e
            except ValueError:
                self.app.log_gui.log(f"未找到色块 '{color_name}'", level="error")
                self.app.log_gui.log(f"  可用色块: {color_patches}", level="info")
                return 0.0
            except IndexError:
                self.app.log_gui.log(f"索引超出范围: {index}/{len(delta_e_values)}", level="error")
                return 0.0

        except Exception as e:
            self.app.log_gui.log(f"读取 ΔE 失败: {str(e)}", level="error")
            import traceback

            self.app.log_gui.log(traceback.format_exc(), level="error")
            return 0.0

    def _calculate_delta_e_for_color(
        self, color_name, measured_x, measured_y, measured_lv
    ):
        """计算单个色块的 ΔE 2000"""
        try:
            white_lv = measured_lv
            measured = self.app.results.get_intermediate_data("accuracy", "measured")
            if measured and len(measured) > 0 and measured[0][2]:
                white_lv = measured[0][2]

            delta_e = self.app.calculate_accuracy_delta_e_2000(
                color_name,
                measured_x,
                measured_y,
                measured_lv,
                white_lv,
            )

            return delta_e

        except Exception as e:
            self.app.log_gui.log(f"计算 ΔE 失败: {str(e)}", level="error")
            return 0.0
