"""测试执行（runner）相关逻辑（Step 5 重构）。

从 pqAutomationApp.PQAutomationApp 中搬迁。每个函数第一行 `self = app`
以保留原有 `self.xxx` 属性访问不变。
"""

import datetime
import time
import traceback
from tkinter import messagebox
import tkinter as tk

import colour
import numpy as np

import algorithm.pq_algorithm as pq_algorithm
from app.data_range_converter import convert_pattern_params
from app.pq.pq_result import PQResult

def new_pq_results(self, test_type, test_name):
    self.results = PQResult(test_type, test_name)
    # 设置配置
    config = {
        "test_type": test_type,
        "test_name": test_name,
        "test_items": self.config.current_test_types[test_type]["test_items"],
        "test_items_chinese": self.config.get_test_item_chinese_names(
            self.config.current_test_types[test_type]["test_items"]
        ),
    }
    self.results.set_test_config(config)

    # 添加测试项
    for item in config["test_items"]:
        self.results.add_test_item(
            item, config["test_items_chinese"][config["test_items"].index(item)]
        )


def run_test(self, test_type, test_items):
    """执行测试"""
    try:
        self.log_gui.log(f"开始执行{self.get_test_type_name(test_type)}测试")
        self.log_gui.log(
            f"测试项目: {', '.join(self.config.get_test_item_chinese_names(test_items))}"
        )

        # 根据测试类型执行不同的测试流程
        if test_type == "screen_module":
            self.run_screen_module_test(test_items)
        elif test_type == "sdr_movie":
            self.run_sdr_movie_test(test_items)
        elif test_type == "hdr_movie":
            self.run_hdr_movie_test(test_items)

        # 测试完成后更新UI状态
        if self.testing:  # 如果没有被中途停止
            self._dispatch_ui(self.on_test_completed)
    except Exception as e:
        self.log_gui.log(f"测试过程中发生错误: {str(e)}")
        import traceback

        self.log_gui.log(traceback.format_exc())
        self._dispatch_ui(self.on_test_error)


def run_screen_module_test(self, test_items):
    """执行屏模组性能测试 - 优化版"""
    self.log_gui.log("执行屏模组性能测试...")

    if test_items:
        self.new_pq_results("screen_module", "屏模组性能测试")
    else:
        self.log_gui.log("未选择任何测试项目")
        return

    # 判断是否需要灰阶数据
    needs_gray_data = any(
        item in test_items for item in ["gamma", "cct", "contrast"]
    )
    shared_gray_data = None  # 共享的灰阶数据

    # 计算总测试项数量
    total_items = len(test_items)
    current_item = 0

    for item in test_items:
        if not self.testing:  # 检查是否被停止
            return

        current_item += 1
        self.status_var.set(f"测试进行中... ({current_item}/{total_items})")

        # ==================== 色域测试 ====================
        if item == "gamut":
            self.test_gamut("screen_module")

        # ==================== 灰阶数据采集 ====================
        # 如果是第一个需要灰阶数据的测试项，统一采集数据
        elif (
            item in ["gamma", "cct", "contrast"]
            and shared_gray_data is None
            and needs_gray_data
        ):
            self.log_gui.log("=" * 50)
            self.log_gui.log("开始统一采集灰阶数据（用于 Gamma/CCT/对比度测试）")
            self.log_gui.log("=" * 50)

            shared_gray_data = self.send_fix_pattern("gray")

            if not shared_gray_data or len(shared_gray_data) < 2:
                self.log_gui.log("灰阶数据采集失败或数据不足，跳过相关测试")
                return

            self.log_gui.log(
                f"✓ 灰阶数据采集完成，共 {len(shared_gray_data)} 个数据点"
            )

            # 保存到 results 对象，供所有灰阶测试使用
            self.results.add_intermediate_data("shared", "gray", shared_gray_data)

            # 执行当前测试项
            if item == "gamma":
                self.test_gamma("screen_module", shared_gray_data)
            elif item == "cct":
                self.test_cct("screen_module", shared_gray_data)
            elif item == "contrast":
                self.test_contrast("screen_module", shared_gray_data)

        # ==================== 后续灰阶测试（复用数据） ====================
        elif item in ["gamma", "cct", "contrast"] and shared_gray_data is not None:
            self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试")

            if item == "gamma":
                self.test_gamma("screen_module", shared_gray_data)
            elif item == "cct":
                self.test_cct("screen_module", shared_gray_data)
            elif item == "contrast":
                self.test_contrast("screen_module", shared_gray_data)


def run_custom_sdr_test(self, test_items):
    """执行客户定制 SDR 测试 - 升级版"""
    self.log_gui.log("执行客户定制 SDR 测试...")
    # 获取信号格式设置
    color_space = self.sdr_color_space_var.get()  # BT.709/BT.601/BT.2020
    gamma_type = self.sdr_gamma_type_var.get()  # 2.2/2.4/2.6
    data_range = self.sdr_data_range_var.get()  # Full/Limited
    bit_depth = self.sdr_bit_depth_var.get()  # 8bit/10bit/12bit

    self.log_gui.log(f"信号格式: 色彩空间={color_space}, Gamma={gamma_type}")
    self.log_gui.log(f"          数据范围={data_range}, 编码位深={bit_depth}")
    self.log_gui.log("开始统一采集灰阶数据（用于 Gamma/CCT/对比度测试）")
    self.test_custom_sdr()

    if self.testing:
        self._dispatch_ui(self.on_custom_template_test_completed)


def run_sdr_movie_test(self, test_items):
    """执行SDR Movie测试"""
    self.log_gui.log("执行SDR Movie测试...")

    if test_items:
        self.new_pq_results("sdr_movie", "SDR Movie测试")
    else:
        self.log_gui.log("未选择任何测试项目")
        return

    # 获取信号格式设置
    color_space = self.sdr_color_space_var.get()  # BT.709/BT.601/BT.2020
    gamma_type = self.sdr_gamma_type_var.get()  # 2.2/2.4/2.6
    data_range = self.sdr_data_range_var.get()  # Full/Limited
    bit_depth = self.sdr_bit_depth_var.get()  # 8bit/10bit/12bit

    self.log_gui.log(f"信号格式: 色彩空间={color_space}, Gamma={gamma_type}")
    self.log_gui.log(f"          数据范围={data_range}, 编码位深={bit_depth}")

    # 判断是否需要灰阶数据
    needs_gray_data = any(
        item in test_items for item in ["gamma", "cct", "contrast"]
    )
    shared_gray_data = None

    # 计算总测试项数量
    total_items = len(test_items)
    current_item = 0

    for item in test_items:
        if not self.testing:
            return

        current_item += 1
        self.status_var.set(f"测试进行中... ({current_item}/{total_items})")

        if item == "gamut":
            self.test_gamut("sdr_movie")

        elif (
            item in ["gamma", "cct", "contrast"]
            and shared_gray_data is None
            and needs_gray_data
        ):
            self.log_gui.log("开始统一采集灰阶数据（用于 Gamma/CCT/对比度测试）")
            shared_gray_data = self.send_fix_pattern("gray")

            if not shared_gray_data or len(shared_gray_data) < 2:
                self.log_gui.log("灰阶数据采集失败或数据不足")
                return

            self.results.add_intermediate_data("shared", "gray", shared_gray_data)

            if item == "gamma":
                self.test_gamma("sdr_movie", shared_gray_data)
            elif item == "cct":
                self.test_cct("sdr_movie", shared_gray_data)
            elif item == "contrast":
                self.test_contrast("sdr_movie", shared_gray_data)

        elif item in ["gamma", "cct", "contrast"] and shared_gray_data is not None:
            self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试")
            if item == "gamma":
                self.test_gamma("sdr_movie", shared_gray_data)
            elif item == "cct":
                self.test_cct("sdr_movie", shared_gray_data)
            elif item == "contrast":
                self.test_contrast("sdr_movie", shared_gray_data)

        elif item == "accuracy":
            self.test_color_accuracy("sdr_movie")


def run_hdr_movie_test(self, test_items):
    """执行HDR Movie测试"""
    self.log_gui.log("执行HDR Movie测试...")

    if test_items:
        self.new_pq_results("hdr_movie", "HDR Movie测试")
    else:
        self.log_gui.log("未选择任何测试项目")
        return

    # 获取信号格式设置
    color_space = self.hdr_color_space_var.get()
    max_cll = self.hdr_maxcll_var.get()
    max_fall = self.hdr_maxfall_var.get()
    data_range = self.hdr_data_range_var.get()
    bit_depth = self.hdr_bit_depth_var.get()

    self.log_gui.log(f"信号格式: 色彩空间={color_space}")
    self.log_gui.log(f"          MaxCLL={max_cll}, MaxFALL={max_fall}")
    self.log_gui.log(f"          数据范围={data_range}, 编码位深={bit_depth}")

    # 判断是否需要灰阶数据
    needs_gray_data = any(
        item in test_items for item in ["eotf", "cct", "contrast"]
    )
    shared_gray_data = None

    # 计算总测试项数量
    total_items = len(test_items)
    current_item = 0

    for item in test_items:
        if not self.testing:
            return

        current_item += 1
        self.status_var.set(f"测试进行中... ({current_item}/{total_items})")

        if item == "gamut":
            self.test_gamut("hdr_movie")

        elif (
            item in ["eotf", "cct", "contrast"]
            and shared_gray_data is None
            and needs_gray_data
        ):
            self.log_gui.log("开始统一采集灰阶数据（用于 EOTF/CCT/对比度测试）")
            shared_gray_data = self.send_fix_pattern("gray")

            if not shared_gray_data or len(shared_gray_data) < 2:
                self.log_gui.log("灰阶数据采集失败或数据不足")
                return

            self.results.add_intermediate_data("shared", "gray", shared_gray_data)

            if item == "eotf":
                self.test_eotf("hdr_movie", shared_gray_data)
            elif item == "cct":
                self.test_cct("hdr_movie", shared_gray_data)
            elif item == "contrast":
                self.test_contrast("hdr_movie", shared_gray_data)

        elif item in ["eotf", "cct", "contrast"] and shared_gray_data is not None:
            self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试")
            if item == "eotf":
                self.test_eotf("hdr_movie", shared_gray_data)
            elif item == "cct":
                self.test_cct("hdr_movie", shared_gray_data)
            elif item == "contrast":
                self.test_contrast("hdr_movie", shared_gray_data)

        elif item == "accuracy":
            self.test_color_accuracy("hdr_movie")


def send_fix_pattern(self, mode):
    """发送固定图案并采集数据 - 支持不同测试类型的信号格式"""
    results = []

    try:
        # 1. 设置图案模式
        if mode == "rgb":
            self.config.set_current_pattern("rgb")
        elif mode == "gray":
            self.config.set_current_pattern("gray")
        elif mode == "accuracy":  # 色准模式（SDR 和 HDR 通用 29色）
            self.config.set_current_pattern("accuracy")
        elif mode == "custom":
            self.config.set_current_pattern("custom")
        else:
            self.log_gui.log(f"❌ 未知的图案模式: {mode}")
            return None

        # 2. 获取当前测试类型
        test_type = self.config.current_test_type

        # 3. 根据测试类型设置信号格式和图案
        if test_type == "screen_module":
            # 屏模组测试：使用 Timing
            self.log_gui.log("=" * 50)
            self.log_gui.log("设置屏模组信号格式:")
            self.log_gui.log("=" * 50)

            timing_str = self.config.current_test_types[test_type]["timing"]
            self.log_gui.log(f"  Timing: {timing_str}")

            # ✅ 屏模组测试：直接使用原始配置
            self.ucd.set_ucd_params(self.config)

        elif test_type == "sdr_movie":
            # SDR 测试：设置色彩空间、Gamma 等
            self.log_gui.log("=" * 50)
            self.log_gui.log("设置 SDR 信号格式:")
            self.log_gui.log("=" * 50)

            color_space = self.sdr_color_space_var.get()
            gamma = self.sdr_gamma_type_var.get()
            data_range = self.sdr_data_range_var.get()
            bit_depth = self.sdr_bit_depth_var.get()

            self.log_gui.log(f"  色彩空间: {color_space}")
            self.log_gui.log(f"  Gamma: {gamma}")
            self.log_gui.log(f"  数据范围: {data_range}")
            self.log_gui.log(f"  编码位深: {bit_depth}")

            success = self.ucd.set_sdr_format(
                color_space=color_space,
                gamma=gamma,
                data_range=data_range,
                bit_depth=bit_depth,
            )

            if success:
                self.log_gui.log("✓ SDR 信号格式设置成功")
            else:
                self.log_gui.log("✗ SDR 信号格式设置失败")

            # 设置图案参数
            if mode == "accuracy":
                self.log_gui.log(f"设置 SDR 29色色准测试图案...")
            else:
                self.log_gui.log(f"设置 SDR 测试图案（{mode} 模式）...")

            # ========== ✅✅✅ 修改：使用临时配置对象 ==========
            import copy

            # 从原始配置获取参数（每次都是干净的）
            if mode == "rgb":
                original_params = copy.deepcopy(
                    self.config.default_pattern_rgb["pattern_params"]
                )
            elif mode == "gray":
                original_params = copy.deepcopy(
                    self.config.default_pattern_gray["pattern_params"]
                )
            elif mode == "accuracy":
                original_params = copy.deepcopy(
                    self.config.default_pattern_accuracy["pattern_params"]
                )
            elif mode == "custom":
                original_params = copy.deepcopy(
                    self.config.default_pattern_temp["pattern_params"]
                )

            self.log_gui.log(f"🔍 使用原始 RGB 参数（前 3 个）:")
            for i in range(min(3, len(original_params))):
                self.log_gui.log(f"  [{i+1}] {original_params[i]}")

            # 根据 data_range 转换
            converted_params = convert_pattern_params(
                pattern_params=original_params, data_range=data_range, verbose=False
            )

            if data_range == "Limited":
                self.log_gui.log("🔧 转换为 Limited Range (16-235):")
                for i in range(min(3, len(converted_params))):
                    self.log_gui.log(
                        f"  {original_params[i]} → {converted_params[i]}"
                    )
            else:
                self.log_gui.log("✓ Full Range，RGB 保持不变")

            # ✅ 创建临时配置对象（不修改 self.config）
            temp_config = self.config.get_temp_config_with_converted_params(
                mode=mode, converted_params=converted_params
            )

            # ✅ 使用临时配置设置参数
            self.ucd.set_ucd_params(temp_config)

            self.log_gui.log(f"✓ 图案参数已设置，共 {len(converted_params)} 个图案")
            # ========== 修改结束 ==========

        elif test_type == "hdr_movie":
            # HDR 测试：设置色彩空间、Metadata 等
            self.log_gui.log("=" * 50)
            self.log_gui.log("设置 HDR 信号格式:")
            self.log_gui.log("=" * 50)

            color_space = self.hdr_color_space_var.get()
            data_range = self.hdr_data_range_var.get()
            bit_depth = self.hdr_bit_depth_var.get()
            max_cll = self.hdr_maxcll_var.get()
            max_fall = self.hdr_maxfall_var.get()

            self.log_gui.log(f"  色彩空间: {color_space}")
            self.log_gui.log(f"  数据范围: {data_range}")
            self.log_gui.log(f"  编码位深: {bit_depth}")
            self.log_gui.log(f"  MaxCLL: {max_cll}")
            self.log_gui.log(f"  MaxFALL: {max_fall}")

            success = self.ucd.set_hdr_format(
                color_space=color_space,
                data_range=data_range,
                bit_depth=bit_depth,
                max_cll=max_cll,
                max_fall=max_fall,
            )

            if success:
                self.log_gui.log("✓ HDR 信号格式设置成功")
            else:
                self.log_gui.log("✗ HDR 信号格式设置失败")

            # 设置图案参数
            if mode == "accuracy":
                self.log_gui.log(f"设置 HDR 29色色准测试图案...")
            else:
                self.log_gui.log(f"设置 HDR 测试图案（{mode} 模式）...")

            # ========== ✅✅✅ 修改：使用临时配置对象 ==========
            import copy

            # 从原始配置获取参数
            if mode == "rgb":
                original_params = copy.deepcopy(
                    self.config.default_pattern_rgb["pattern_params"]
                )
            elif mode == "gray":
                original_params = copy.deepcopy(
                    self.config.default_pattern_gray["pattern_params"]
                )
            elif mode == "accuracy":
                original_params = copy.deepcopy(
                    self.config.default_pattern_accuracy["pattern_params"]
                )

            self.log_gui.log(f"🔍 使用原始 RGB 参数（前 3 个）:")
            for i in range(min(3, len(original_params))):
                self.log_gui.log(f"  [{i+1}] {original_params[i]}")

            # 根据 data_range 转换
            converted_params = convert_pattern_params(
                pattern_params=original_params, data_range=data_range, verbose=False
            )

            if data_range == "Limited":
                self.log_gui.log("🔧 转换为 Limited Range (16-235):")
                for i in range(min(3, len(converted_params))):
                    self.log_gui.log(
                        f"  {original_params[i]} → {converted_params[i]}"
                    )
            else:
                self.log_gui.log("✓ Full Range，RGB 保持不变")

            # ✅ 创建临时配置对象
            temp_config = self.config.get_temp_config_with_converted_params(
                mode=mode, converted_params=converted_params
            )

            self.ucd.set_ucd_params(temp_config)

            self.log_gui.log(f"✓ 图案参数已设置，共 {len(converted_params)} 个图案")
            # ========== 修改结束 ==========

        self.log_gui.log("=" * 50)

        # 4. 循环发送图案并采集数据（使用原始配置的数量）
        total_patterns = len(self.config.current_pattern["pattern_params"])
        self.log_gui.log(f"开始采集数据，共 {total_patterns} 个图案")
        settle_time = max(0.2, float(getattr(self, "pattern_settle_time", 1.0)))
        progress_step = max(
            1, int(getattr(self, "pattern_progress_log_step", 5))
        )
        self.log_gui.log(
            f"采集等待时间: {settle_time:.2f}s（可通过 pattern_settle_time 调整）"
        )

        # 获取颜色名称列表（用于日志显示）
        color_names = None
        if mode == "accuracy":
            color_names = self.config.get_accuracy_color_names()

        custom_pattern_names = []
        if mode == "custom" and hasattr(self.config, "get_temp_pattern_names"):
            custom_pattern_names = self.config.get_temp_pattern_names()

        for i in range(total_patterns):
            if not self.testing:
                self.log_gui.log("⚠️ 测试已停止")
                return results

            should_log_detail = (
                i == 0
                or (i + 1) == total_patterns
                or ((i + 1) % progress_step == 0)
            )

            # 设置下一个图案（显示颜色名称）
            if should_log_detail:
                if color_names and i < len(color_names):
                    self.log_gui.log(
                        f"发送第 {i+1}/{total_patterns} 个图案: {color_names[i]}..."
                    )
                else:
                    self.log_gui.log(f"发送第 {i+1}/{total_patterns} 个图案...")

            self.ucd.set_next_pattern()
            self.ucd.run()
            time.sleep(settle_time)

            # 测量数据
            if mode == "custom":
                result = []
                self.ca.set_Display(1)
                tcp, duv, lv, X, Y, Z = self.ca.readAllDisplay()

                if should_log_detail:
                    self.log_gui.log(
                        f"  ✓ 测量完成: TCP={tcp:.4f}, DUV={duv:.4f}, lv={lv:.2f}, "
                        f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}"
                    )

                self.ca.set_Display(8)
                lambda_, Pe, lv, X, Y, Z = self.ca.readAllDisplay()

                if should_log_detail:
                    self.log_gui.log(
                        f"  ✓ 测量完成: λ={lambda_:.4f}, Pe={Pe:.4f}, lv={lv:.2f}, "
                        f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}"
                    )
                result = [tcp, duv, lv, lambda_, Pe, lv, X, Y, Z]
                results.append(result)

                # 每完成一个 pattern，实时写入客户模板结果表。
                try:
                    xy = colour.XYZ_to_xy(np.array([X, Y, Z]))
                    u_prime, v_prime, _ = colour.XYZ_to_CIE1976UCS(
                        np.array([X, Y, Z])
                    )
                    row_data = {
                        "pattern_name": (
                            custom_pattern_names[i]
                            if i < len(custom_pattern_names)
                            else f"P {i + 1}"
                        ),
                        "X": X,
                        "Y": Y,
                        "Z": Z,
                        "x": xy[0],
                        "y": xy[1],
                        "Lv": lv,
                        "u_prime": u_prime,
                        "v_prime": v_prime,
                        "Tcp": tcp,
                        "duv": duv,
                        "lambda_d": lambda_,
                        "Pe": Pe,
                    }
                    self.root.after(
                        0,
                        lambda row_no=i + 1, data=row_data: self.append_custom_template_result(
                            row_no, data
                        ),
                    )
                except Exception as e:
                    self.log_gui.log(f"⚠️ 第 {i+1} 行实时结果写入失败: {str(e)}")

            else:
                self.ca.set_xyLv_Display()

                x, y, lv, X, Y, Z = self.ca.readAllDisplay()
                results.append([x, y, lv, X, Y, Z])

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

        self.log_gui.log(f"✓ 数据采集完成，共 {len(results)} 组数据")
        return results

    except Exception as e:
        self.log_gui.log(f"❌ 发送图案失败: {str(e)}")
        import traceback

        self.log_gui.log(traceback.format_exc())
        return None


def test_custom_sdr(self):
    """执行客户定制 SDR 测试 - 升级版"""
    self.log_gui.log("执行客户定制 SDR 测试...")
    results = self.send_fix_pattern("custom")
    if not results:
        self.log_gui.log("客户模板SDR测试被中断")
        return

    self.log_gui.log(f"客户模板采集完成，共 {len(results)} 组数据")


def test_gamut(self, test_type):
    """测试色域"""
    self.log_gui.log("开始测试色域...")
    self.results.start_test_item("gamut")

    try:
        # 存储测量结果
        results = self.send_fix_pattern("rgb")

        # 检查结果是否为空
        if not results:
            self.log_gui.log("色域测试被中断")
            return

        self.results.add_intermediate_data("gamut", "rgb", results)

        # 计算色域覆盖率
        self.log_gui.log("计算色域覆盖率...")

        # 提取 x, y 坐标用于计算色域
        xy_points = [[result[0], result[1]] for result in results]

        # ========== ✅ 测试时：使用色彩空间的值作为参考标准 ==========
        reference_standard = None
        area = None
        coverage = None

        if test_type == "screen_module":
            # 屏模组测试：固定使用 DCI-P3（因为没有色彩空间设置）
            reference_standard = "DCI-P3"

            # ✅ 同步更新到色域参考标准变量（供后续重绘使用）
            self.screen_gamut_ref_var.set(reference_standard)

        elif test_type == "sdr_movie":
            # SDR 测试：使用色彩空间设置
            color_space = self.sdr_color_space_var.get()

            if color_space == "BT.709":
                reference_standard = "BT.709"
            elif color_space == "BT.601":
                reference_standard = "BT.601"
            elif color_space == "BT.2020":
                reference_standard = "BT.2020"
            else:
                reference_standard = "BT.709"
                self.log_gui.log(
                    f"⚠️ 未识别的色彩空间 '{color_space}'，使用默认标准 BT.709"
                )

            # ✅ 同步更新到色域参考标准变量
            self.sdr_gamut_ref_var.set(reference_standard)

        elif test_type == "hdr_movie":
            # HDR 测试：使用色彩空间设置
            color_space = self.hdr_color_space_var.get()

            if color_space == "BT.2020":
                reference_standard = "BT.2020"
            elif color_space == "DCI-P3":
                reference_standard = "DCI-P3"
            else:
                reference_standard = "BT.2020"
                self.log_gui.log(
                    f"⚠️ 未识别的色彩空间 '{color_space}'，使用默认标准 BT.2020"
                )

            # ✅ 同步更新到色域参考标准变量
            self.hdr_gamut_ref_var.set(reference_standard)
        else:
            # 未知测试类型，使用 DCI-P3 作为后备
            reference_standard = "DCI-P3"
            self.log_gui.log(
                f"⚠️ 未识别的测试类型 '{test_type}'，使用默认标准 DCI-P3"
            )

        # ========== 根据参考标准计算 XY 覆盖率 ==========
        if reference_standard == "BT.2020":
            area, coverage = pq_algorithm.calculate_gamut_coverage_BT2020(xy_points)
        elif reference_standard == "BT.709":
            area, coverage = pq_algorithm.calculate_gamut_coverage_BT709(xy_points)
        elif reference_standard == "DCI-P3":
            area, coverage = pq_algorithm.calculate_gamut_coverage_DCIP3(xy_points)
        elif reference_standard == "BT.601":
            area, coverage = pq_algorithm.calculate_gamut_coverage_BT601(xy_points)
        else:
            # 默认使用 DCI-P3
            area, coverage = pq_algorithm.calculate_gamut_coverage_DCIP3(xy_points)
            reference_standard = "DCI-P3"
            self.log_gui.log(
                f"⚠️ 未识别的参考标准 '{reference_standard}'，使用默认标准 DCI-P3"
            )

        # ========== ✅✅✅ 新增：计算 UV 覆盖率 ==========
        uv_coverage = 0
        try:
            # 将 XY 转换为 UV
            uv_points = []
            for x, y in xy_points:
                u, v = pq_algorithm.xy_to_uv_1976(x, y)
                uv_points.append([u, v])

            # 根据参考标准计算 UV 覆盖率
            if len(uv_points) >= 3:
                if reference_standard == "BT.2020":
                    _, uv_coverage = (
                        pq_algorithm.calculate_gamut_coverage_BT2020_uv(uv_points)
                    )
                elif reference_standard == "BT.709":
                    _, uv_coverage = pq_algorithm.calculate_gamut_coverage_BT709_uv(
                        uv_points
                    )
                elif reference_standard == "DCI-P3":
                    _, uv_coverage = pq_algorithm.calculate_gamut_coverage_DCIP3_uv(
                        uv_points
                    )
                elif reference_standard == "BT.601":
                    _, uv_coverage = pq_algorithm.calculate_gamut_coverage_BT601_uv(
                        uv_points
                    )
                else:
                    _, uv_coverage = pq_algorithm.calculate_gamut_coverage_DCIP3_uv(
                        uv_points
                    )

                self.log_gui.log(
                    f"✓ XY 覆盖率: {coverage:.1f}% | UV 覆盖率: {uv_coverage:.1f}%"
                )
        except:
            uv_coverage = 0

        # ========== 保存结果时包含 XY 和 UV 覆盖率 ==========
        self.results.set_test_item_result(
            "gamut",
            {
                "area": area,
                "coverage": coverage,
                "uv_coverage": uv_coverage,  # ✅ 新增 UV 覆盖率
                "reference": reference_standard,
            },
        )

        # 传递完整的 results 用于绘图
        self.plot_gamut(results, coverage, test_type)

        self.log_gui.log("色域测试完成")

    except Exception as e:
        self.log_gui.log(f"色域测试失败: {str(e)}")
        import traceback

        self.log_gui.log(traceback.format_exc())
        raise


def test_gamma(self, test_type, gray_data=None):
    """测试Gamma曲线

    Args:
        test_type: 测阶数据，如果提供则使用，否则重新采集
    """
    self.log_gui.log("开始测试Gamma曲线...")
    self.results.start_test_item("gamma")

    try:
        # 使用传入的灰阶数据或独立采集
        if gray_data is not None:
            self.log_gui.log("使用共享的灰阶数据")
            results = gray_data
        else:
            self.log_gui.log("独立采集灰阶数据")
            results = self.send_fix_pattern("gray")

            if not results or len(results) < 2:
                self.log_gui.log("Gamma测试被中断或数据不足")
                return

            self.results.add_intermediate_data("gamma", "gray", results)

        self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行计算")
        self.log_gui.log("计算Gamma值...")

        # ========== ✅ 修复：正确获取 max_index_fix ==========
        # 获取配置中的值
        config_max_value = self.config.current_pattern.get(
            "measurement_max_value", 10
        )

        # 强制转换为整数
        try:
            max_index_fix = int(config_max_value)
        except (ValueError, TypeError):
            self.log_gui.log(f"警告: measurement_max_value 转换失败，使用默认值 10")
            max_index_fix = 10

        self.log_gui.log(f"配置中的 max_index_fix = {max_index_fix}")

        # 关键修复：验证并调整 max_index_fix
        # max_index_fix 应该是数据点的最大索引（从0开始，所以是 len - 1）
        actual_max_index = len(results) - 1

        if max_index_fix > actual_max_index:
            self.log_gui.log(
                f"警告: 配置的 max_index_fix({max_index_fix}) > 实际最大索引({actual_max_index})"
            )
            self.log_gui.log(f"自动调整为: {actual_max_index}")
            max_index_fix = actual_max_index

        self.log_gui.log(f"最终使用的 max_index_fix = {max_index_fix}")
        # ========================================================

        # 获取灰阶 pattern 参数（用于22293 Gamma数据对齐）
        pattern_params = self.config.default_pattern_gray.get(
            "pattern_params", None
        )

        # 计算Gamma值（使用修正后的 max_index_fix 和 8bit pattern参数）
        results_with_gamma_list, L_bar = self.calculate_gamma(
            results, max_index_fix, pattern_params
        )
        self.results.set_test_item_result(
            "gamma", {"gamma": results_with_gamma_list, "L_bar": L_bar}
        )

        # 绘制Gamma曲线
        if test_type == "sdr_movie":
            try:
                target_gamma = float(self.sdr_gamma_type_var.get())
            except (ValueError, AttributeError):
                target_gamma = 2.2
        else:
            target_gamma = 2.2

        self.plot_gamma(L_bar, results_with_gamma_list, target_gamma, test_type)

        self.log_gui.log("Gamma测试完成")

    except Exception as e:
        self.log_gui.log(f"Gamma测试失败: {str(e)}")
        import traceback

        self.log_gui.log(traceback.format_exc())
        raise


def test_eotf(self, test_type, gray_data=None):
    """测试 EOTF 曲线（HDR 专用）

    Args:
        test_type: 测试类型阶数据，如果提供则使用，否则重新采集
    """
    self.log_gui.log("开始测试 EOTF 曲线（HDR）...")
    self.results.start_test_item("eotf")

    try:
        # 使用传入的灰阶数据或独立采集
        if gray_data is not None:
            self.log_gui.log("使用共享的灰阶数据")
            results = gray_data
        else:
            self.log_gui.log("独立采集灰阶数据")
            results = self.send_fix_pattern("gray")

            if not results or len(results) < 2:
                self.log_gui.log("EOTF 测试被中断或数据不足")
                return

            self.results.add_intermediate_data("eotf", "gray", results)

        self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行计算")
        self.log_gui.log("计算 EOTF 值...")

        # ========== 获取 max_index_fix ==========
        config_max_value = self.config.current_pattern.get(
            "measurement_max_value", 10
        )

        try:
            max_index_fix = int(config_max_value)
        except (ValueError, TypeError):
            self.log_gui.log(f"警告: measurement_max_value 转换失败，使用默认值 10")
            max_index_fix = 10

        self.log_gui.log(f"配置中的 max_index_fix = {max_index_fix}")

        # 验证并调整 max_index_fix
        actual_max_index = len(results) - 1

        if max_index_fix > actual_max_index:
            self.log_gui.log(
                f"警告: 配置的 max_index_fix({max_index_fix}) > 实际最大索引({actual_max_index})"
            )
            self.log_gui.log(f"自动调整为: {actual_max_index}")
            max_index_fix = actual_max_index

        self.log_gui.log(f"最终使用的 max_index_fix = {max_index_fix}")

        # 获取灰阶 pattern 参数（用于22293 Gamma数据对齐）
        pattern_params = self.config.default_pattern_gray.get(
            "pattern_params", None
        )

        # ========== 计算 EOTF（复用 Gamma 计算逻辑，使用8bit pattern参数）==========
        results_with_eotf_list, L_bar = self.calculate_gamma(
            results, max_index_fix, pattern_params
        )

        # 保存结果
        self.results.set_test_item_result(
            "eotf", {"eotf": results_with_eotf_list, "L_bar": L_bar}
        )

        # ========== 绘制 EOTF 曲线 ==========
        # HDR 使用 PQ 曲线，目标 gamma 设为 None（不使用传统 gamma）
        self.plot_eotf(L_bar, results_with_eotf_list, test_type)

        self.log_gui.log("EOTF 测试完成")

    except Exception as e:
        self.log_gui.log(f"EOTF 测试失败: {str(e)}")
        import traceback

        self.log_gui.log(traceback.format_exc())
        raise


def test_cct(self, test_type, gray_data=None):
    """测试色度一致性"""
    self.log_gui.log("开始测试色度一致性...")
    self.results.start_test_item("cct")

    try:
        if gray_data is not None:
            self.log_gui.log("使用共享的灰阶数据")
            results = gray_data
        else:
            self.log_gui.log("独立采集灰阶数据")
            results = self.send_fix_pattern("gray")

            if not results:
                self.log_gui.log("色度一致性测试被中断")
                return

            self.results.add_intermediate_data("cct", "gray", results)

        self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行色度计算")

        # 提取色度坐标
        cct_values = pq_algorithm.calculate_cct_from_results(results)

        # 保存到结果
        self.results.set_test_item_result("cct", {"cct_values": cct_values})

        # 绘制图表
        self.plot_cct(test_type)

        self.log_gui.log("色度一致性测试完成")
    except Exception as e:
        self.log_gui.log(f"色度一致性测试失败: {str(e)}")
        import traceback

        self.log_gui.log(traceback.format_exc())
        raise


def test_contrast(self, test_type, gray_data=None):
    """测试对比度

    Args:
        test_type: 阶数据，如果提供则使用，否则重新采集
    """
    self.log_gui.log("开始测试对比度...")
    self.results.start_test_item("contrast")

    try:
        # ✅ 优先使用传入的灰阶数据
        if gray_data is not None:
            self.log_gui.log("使用共享的灰阶数据")
            results = gray_data
        else:
            self.log_gui.log("独立采集灰阶数据")
            results = self.send_fix_pattern("gray")

            if not results:
                self.log_gui.log("对比度测试被中断")
                return

            self.results.add_intermediate_data("contrast", "gray", results)

        # 获取最亮和最暗的亮度值
        luminance_values = [result[2] for result in results]  # 提取lv值

        max_luminance = max(luminance_values)  # 最大亮度（白）
        min_luminance = min(luminance_values)  # 最小亮度（黑）

        # 防止除以0
        if min_luminance < 0.001:
            min_luminance = 0.001

        # 计算对比度
        contrast_ratio = max_luminance / min_luminance

        # 保存结果
        contrast_data = {
            "max_luminance": max_luminance,
            "min_luminance": min_luminance,
            "contrast_ratio": contrast_ratio,
            "luminance_values": luminance_values,
        }
        self.results.set_test_item_result("contrast", contrast_data)

        # 显示对比度结果到日志
        self.log_gui.log(f"最大亮度 (白场): {max_luminance:.2f} cd/m²")
        self.log_gui.log(f"最小亮度 (黑场): {min_luminance:.4f} cd/m²")
        self.log_gui.log(f"对比度: {contrast_ratio:.0f}:1")

        # 绘制对比度图表
        self.plot_contrast(contrast_data, test_type)

        self.log_gui.log("对比度测试完成")
    except Exception as e:
        self.log_gui.log(f"对比度测试失败: {str(e)}")
        import traceback

        self.log_gui.log(traceback.format_exc())
        raise


def test_color_accuracy(self, test_type):
    """测试色准 - 使用手工实现的 ΔE 2000（应用 Gamma）"""

    # ========== 读取用户选择的 Gamma ==========
    if test_type == "sdr_movie":
        try:
            target_gamma = float(self.sdr_gamma_type_var.get())
        except (ValueError, AttributeError):
            target_gamma = 2.2

        self.log_gui.log("=" * 50)
        self.log_gui.log(f"开始测试色准（SDR Movie 标准 - 29色）")
        self.log_gui.log(f"✓ 使用 Gamma: {target_gamma}")  # ← 新增
        self.log_gui.log("=" * 50)

    elif test_type == "hdr_movie":
        target_gamma = 2.4  # HDR 使用 PQ，但保留参考值

        self.log_gui.log("=" * 50)
        self.log_gui.log(f"开始测试色准（HDR Movie 标准 - 29色）")
        self.log_gui.log(f"✓ 使用 Gamma: PQ (参考γ={target_gamma})")  # ← 新增
        self.log_gui.log("=" * 50)

    else:  # screen_module
        target_gamma = 2.2

        self.log_gui.log("=" * 50)
        self.log_gui.log(f"开始测试色准（屏模组 标准 - 29色）")
        self.log_gui.log(f"✓ 使用 Gamma: {target_gamma}")
        self.log_gui.log("=" * 50)

    # 获取 29色名称
    color_names = self.config.get_accuracy_color_names()

    self.log_gui.log(f"✓ 将测试 {len(color_names)} 个色块")
    self.log_gui.log(f"  色块分组:")
    self.log_gui.log(f"    灰阶 (5个): {', '.join(color_names[:5])}")
    self.log_gui.log(f"    ColorChecker (18个): {', '.join(color_names[5:23])}")
    self.log_gui.log(f"    饱和色 (6个): {', '.join(color_names[23:])}")

    self.log_gui.log("=" * 50)
    self.log_gui.log("开始发送色准图案并采集数据...")
    self.log_gui.log("=" * 50)

    # 发送 29色图案
    measured_data_list = self.send_fix_pattern("accuracy")

    if measured_data_list is None or len(measured_data_list) != 29:
        self.log_gui.log(f"❌ 数据数量不匹配")
        self.log_gui.log(f"   期望: 29 个")
        self.log_gui.log(
            f"   实际: {len(measured_data_list) if measured_data_list else 0} 个"
        )
        return

    # 保存原始测量数据供单步调试使用
    self.results.add_intermediate_data("accuracy", "measured", measured_data_list)

    # ========== 计算 ΔE 2000（显示 Gamma）==========
    self.log_gui.log("=" * 50)
    self.log_gui.log(f"计算色准（ΔE 2000，Gamma {target_gamma}）...")
    self.log_gui.log("=" * 50)

    # 获取标准 xy 坐标
    standards = self.get_accuracy_color_standards(test_type)

    delta_e_values = []
    color_patches = []

    for i, (name, measured_data) in enumerate(zip(color_names, measured_data_list)):
        measured_x = measured_data[0]
        measured_y = measured_data[1]
        measured_lv = measured_data[2]

        standard_x, standard_y = standards.get(name, (0.3127, 0.3290))

        delta_e = self.calculate_delta_e_2000(
            measured_x,
            measured_y,
            measured_lv,
            standard_x,
            standard_y,
        )

        delta_e_values.append(delta_e)
        color_patches.append(name)

        if delta_e < 3:
            grade, icon = "优秀", "✓"
        elif delta_e < 5:
            grade, icon = "良好", "○"
        else:
            grade, icon = "偏差", "✗"

        self.log_gui.log(
            f"  [{i+1:2d}] {name:20s} ΔE={delta_e:5.2f}  {icon} {grade}"
        )

    # ========== 统计 ==========
    avg_delta_e_all = sum(delta_e_values) / len(delta_e_values)
    max_delta_e_all = max(delta_e_values)
    min_delta_e_all = min(delta_e_values)

    excellent_count_all = sum(1 for de in delta_e_values if de < 3)
    good_count_all = sum(1 for de in delta_e_values if 3 <= de < 5)
    poor_count_all = sum(1 for de in delta_e_values if de >= 5)

    delta_e_gray = delta_e_values[0:5]
    avg_delta_e_gray = sum(delta_e_gray) / len(delta_e_gray)

    delta_e_colorchecker = delta_e_values[5:23]
    avg_delta_e_colorchecker = sum(delta_e_colorchecker) / len(delta_e_colorchecker)

    delta_e_saturated = delta_e_values[23:29]
    avg_delta_e_saturated = sum(delta_e_saturated) / len(delta_e_saturated)

    self.log_gui.log("=" * 50)
    self.log_gui.log("色准统计（全 29色）:")
    self.log_gui.log("=" * 50)
    self.log_gui.log(f"  平均 ΔE: {avg_delta_e_all:.2f}")
    self.log_gui.log(f"  最大 ΔE: {max_delta_e_all:.2f}")
    self.log_gui.log(f"  最小 ΔE: {min_delta_e_all:.2f}")
    self.log_gui.log(f"  优秀 (ΔE<3): {excellent_count_all} 个")
    self.log_gui.log(f"  良好 (3≤ΔE<5): {good_count_all} 个")
    self.log_gui.log(f"  偏差 (ΔE≥5): {poor_count_all} 个")

    self.log_gui.log("")
    self.log_gui.log("分组统计:")
    self.log_gui.log(f"  灰阶 (5个):        平均 ΔE = {avg_delta_e_gray:.2f}")
    self.log_gui.log(
        f"  ColorChecker (18个): 平均 ΔE = {avg_delta_e_colorchecker:.2f}"
    )
    self.log_gui.log(f"  饱和色 (6个):      平均 ΔE = {avg_delta_e_saturated:.2f}")

    # ========== 保存测试结果 ==========
    accuracy_data = {
        "color_patches": color_patches,
        "delta_e_values": delta_e_values,
        "color_measurements": measured_data_list,
        "avg_delta_e": avg_delta_e_all,
        "max_delta_e": max_delta_e_all,
        "min_delta_e": min_delta_e_all,
        "excellent_count": excellent_count_all,
        "good_count": good_count_all,
        "poor_count": poor_count_all,
        "avg_delta_e_gray": avg_delta_e_gray,
        "avg_delta_e_colorchecker": avg_delta_e_colorchecker,
        "avg_delta_e_saturated": avg_delta_e_saturated,
        "target_gamma": target_gamma,
    }

    self.results.set_test_item_result("accuracy", accuracy_data)

    # ========== 绘制图表 ==========
    self.plot_accuracy(accuracy_data, test_type)

    self.log_gui.log("色准测试完成")


def on_test_completed(self):
    """测试完成后的UI更新"""
    self.testing = False
    self.start_btn.config(state=tk.NORMAL)
    self.stop_btn.config(state=tk.DISABLED)
    self.save_btn.config(state=tk.NORMAL)
    self.clear_config_btn.config(state=tk.NORMAL)
    self.status_var.set("测试完成")
    self.log_gui.log("测试完成")

    # 恢复配置项按钮
    if hasattr(self, "config_panel_frame"):
        try:
            self.config_panel_frame.btn.configure(state="normal")
        except:
            pass

    # 启用色域参考标准下拉框
    try:
        test_type = self.config.current_test_type

        if test_type == "screen_module" and hasattr(self, "screen_gamut_combo"):
            self.screen_gamut_combo.configure(state="readonly")
            self.log_gui.log("✓ 屏模组色域参考标准已启用")

        elif test_type == "sdr_movie" and hasattr(self, "sdr_gamut_combo"):
            self.sdr_gamut_combo.configure(state="readonly")
            self.log_gui.log("✓ SDR 色域参考标准已启用")

        elif test_type == "hdr_movie" and hasattr(self, "hdr_gamut_combo"):
            self.hdr_gamut_combo.configure(state="readonly")
            self.log_gui.log("✓ HDR 色域参考标准已启用")
    except Exception as e:
        self.log_gui.log(f"启用色域参考标准失败: {str(e)}")

    # 获取当前测试类型和选中的测试项
    selected_items = self.get_selected_test_items()
    test_type = self.config.current_test_type

    # ==================== ✅ 启用单步调试按钮 ====================
    if hasattr(self, "debug_panel"):
        try:
            # 屏模组：启用 Gamma 和 RGB 单步调试
            if test_type == "screen_module":
                if "gamma" in selected_items:
                    gray_data = self.results.get_intermediate_data("shared", "gray")
                    if gray_data:
                        self.debug_panel.enable_debug(
                            "screen_module", "gamma", gray_data
                        )

                # 启用 RGB 单步调试（色域测试完成后）
                if "gamut" in selected_items:
                    rgb_data = self.results.get_intermediate_data("gamut", "rgb")
                    if rgb_data:
                        self.debug_panel.enable_debug(
                            "screen_module", "rgb", rgb_data
                        )

                # ✅ 启用单步调试按钮
                if hasattr(self, "screen_debug_btn"):
                    self.screen_debug_btn.config(state=tk.NORMAL)
                    self.log_gui.log("✓ 屏模组单步调试按钮已启用")

            # SDR：启用 Gamma、色准和 RGB 单步调试
            elif test_type == "sdr_movie":
                if "gamma" in selected_items:
                    gray_data = self.results.get_intermediate_data("shared", "gray")
                    if gray_data:
                        self.debug_panel.enable_debug(
                            "sdr_movie", "gamma", gray_data
                        )

                if "accuracy" in selected_items:
                    accuracy_data = self.results.get_intermediate_data(
                        "accuracy", "measured"
                    )
                    if accuracy_data:
                        self.debug_panel.enable_debug(
                            "sdr_movie", "accuracy", accuracy_data
                        )

                # 启用 RGB 单步调试（色域测试完成后）
                if "gamut" in selected_items:
                    rgb_data = self.results.get_intermediate_data("gamut", "rgb")
                    if rgb_data:
                        self.debug_panel.enable_debug("sdr_movie", "rgb", rgb_data)

                # ✅ 启用单步调试按钮
                if hasattr(self, "sdr_debug_btn"):
                    self.sdr_debug_btn.config(state=tk.NORMAL)
                    self.log_gui.log("✓ SDR 单步调试按钮已启用")

            # HDR：启用 EOTF、色准和 RGB 单步调试
            elif test_type == "hdr_movie":
                if "eotf" in selected_items:
                    gray_data = self.results.get_intermediate_data("shared", "gray")
                    if gray_data:
                        self.debug_panel.enable_debug(
                            "hdr_movie", "eotf", gray_data
                        )

                if "accuracy" in selected_items:
                    accuracy_data = self.results.get_intermediate_data(
                        "accuracy", "measured"
                    )
                    if accuracy_data:
                        self.debug_panel.enable_debug(
                            "hdr_movie", "accuracy", accuracy_data
                        )

                # 启用 RGB 单步调试（色域测试完成后）
                if "gamut" in selected_items:
                    rgb_data = self.results.get_intermediate_data("gamut", "rgb")
                    if rgb_data:
                        self.debug_panel.enable_debug("hdr_movie", "rgb", rgb_data)

                # ✅ 启用单步调试按钮
                if hasattr(self, "hdr_debug_btn"):
                    self.hdr_debug_btn.config(state=tk.NORMAL)
                    self.log_gui.log("✓ HDR 单步调试按钮已启用")

        except Exception as e:
            self.log_gui.log(f"启用单步调试失败: {str(e)}")

    # ==================== 显示色度/色域重新计算按钮 ====================
    if "cct" in selected_items:
        try:
            if test_type == "screen_module" and hasattr(self, "recalc_cct_btn"):
                self.recalc_cct_btn.grid()
                self.log_gui.log("✓ 屏模组色度参数调整按钮已启用")
            elif test_type == "sdr_movie" and hasattr(self, "sdr_recalc_cct_btn"):
                self.sdr_recalc_cct_btn.grid()
                self.log_gui.log("✓ SDR 色度参数调整按钮已启用")
            elif test_type == "hdr_movie" and hasattr(self, "hdr_recalc_cct_btn"):
                self.hdr_recalc_cct_btn.grid()
                self.log_gui.log("✓ HDR 色度参数调整按钮已启用")
        except Exception as e:
            self.log_gui.log(f"显示色度重新计算按钮失败: {str(e)}")

    if "gamut" in selected_items:
        try:
            if test_type == "screen_module" and hasattr(self, "recalc_gamut_btn"):
                self.recalc_gamut_btn.grid()
                self.log_gui.log("✓ 屏模组色域参考调整按钮已启用")
            elif test_type == "sdr_movie" and hasattr(self, "sdr_recalc_gamut_btn"):
                self.sdr_recalc_gamut_btn.grid()
                self.log_gui.log("✓ SDR 色域参考调整按钮已启用")
            elif test_type == "hdr_movie" and hasattr(self, "hdr_recalc_gamut_btn"):
                self.hdr_recalc_gamut_btn.grid()
                self.log_gui.log("✓ HDR 色域参考调整按钮已启用")
        except Exception as e:
            self.log_gui.log(f"显示色域重新计算按钮失败: {str(e)}")

    messagebox.showinfo("完成", "测试已完成！")


def on_custom_template_test_completed(self):
    """客户模板测试完成后的UI更新"""
    self.testing = False
    self.set_custom_result_table_locked(False)
    self.start_btn.config(state=tk.NORMAL)
    self.stop_btn.config(state=tk.DISABLED)
    self.save_btn.config(state=tk.DISABLED)
    self.clear_config_btn.config(state=tk.NORMAL)
    self.custom_btn.config(state=tk.NORMAL)
    self.status_var.set("客户模板测试完成")

    if hasattr(self, "config_panel_frame"):
        try:
            self.config_panel_frame.btn.configure(state="normal")
        except:
            pass

    self.log_gui.log("客户模板测试完成")
    messagebox.showinfo("完成", "客户模板测试已完成！")


def get_current_test_result(self):
    """获取当前测试结果"""
    test_type = self.test_type_var.get()
    test_items = self.get_selected_test_items()

    # 构建测试结果字典
    result = {
        "test_type": test_type,
        "test_type_name": self.get_test_type_name(test_type),
        "test_items": test_items,
        "test_items_names": self.config.get_test_item_chinese_names(test_items),
        "timestamp": datetime.datetime.now(),
        "status": "完成",
        "results": {},
    }

    # 根据测试项目收集结果数据
    for item in test_items:
        if item == "gamut" and hasattr(self, "gamut_results"):
            result["results"]["gamut"] = getattr(self, "gamut_results", {})
        elif item in ["gamma", "eotf"] and hasattr(self, "gamma_results"):
            result["results"][item] = getattr(self, "gamma_results", {})
        elif item == "cct" and hasattr(self, "cct_results"):
            result["results"]["cct"] = getattr(self, "cct_results", {})
        elif item == "contrast" and hasattr(self, "contrast_results"):
            result["results"]["contrast"] = getattr(self, "contrast_results", {})
        elif item == "accuracy" and hasattr(self, "accuracy_results"):
            result["results"]["accuracy"] = getattr(self, "accuracy_results", {})

    return result


def on_test_error(self):
    """测试出错后的UI更新"""
    self.testing = False
    self.set_custom_result_table_locked(False)
    self.start_btn.config(state=tk.NORMAL)
    self.stop_btn.config(state=tk.DISABLED)
    self.clear_config_btn.config(state=tk.NORMAL)
    if hasattr(self, "custom_btn"):
        self.custom_btn.config(state=tk.NORMAL)
    self.status_var.set("测试出错")

    # 恢复配置项按钮
    if hasattr(self, "config_panel_frame"):
        try:
            self.config_panel_frame.btn.configure(state="normal")
        except:
            pass

    messagebox.showerror("错误", "测试过程中发生错误，请查看日志")


