# -*- coding: utf-8 -*-
"""
BRDF校正工具 - 统一GUI应用
支持陆地植被BRDF校正（FlexBRDF）和水体BRDF校正（ocbrdf）两个模块
"""
import sys
import os
import json
import subprocess
import threading
from pathlib import Path

from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QGroupBox,
    QFormLayout, QLabel, QLineEdit, QPushButton, QFileDialog, QComboBox,
    QCheckBox, QRadioButton, QSpinBox, QDoubleSpinBox, QTabWidget, QMessageBox, QScrollArea,
    QStackedWidget, QGridLayout, QDialog, QDialogButtonBox,
    QListWidget, QListWidgetItem, QTextEdit, QProgressBar, QSplitter
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QItemSelectionModel
from PyQt5.QtGui import QFont, QIcon, QPalette, QWheelEvent


# ==================== 禁用滚轮的自定义控件 ====================
class NoWheelSpinBox(QSpinBox):
    """禁用鼠标滚轮的SpinBox"""
    def wheelEvent(self, event: QWheelEvent):
        event.ignore()


class NoWheelDoubleSpinBox(QDoubleSpinBox):
    """禁用鼠标滚轮的DoubleSpinBox"""
    def wheelEvent(self, event: QWheelEvent):
        event.ignore()


class NoWheelComboBox(QComboBox):
    """禁用鼠标滚轮的ComboBox"""
    def wheelEvent(self, event: QWheelEvent):
        event.ignore()


# ==================== 掩膜条件编辑器 ====================
class MaskConditionEditor(QWidget):
    """单个掩膜条件的编辑组件，支持类型选择和参数配置"""

    # 掩膜类型及对应的参数定义
    MASK_TYPES = {
        "ndi": {
            "params": [
                ("band_1", int, 550),
                ("band_2", int, 2150),
                ("min", float, -1.0),
                ("max", float, 0.0)
            ]
        },
        "ancillary": {
            "params": [
                ("name", str, "slope"),
                ("min", float, 0.0),
                ("max", float, 1.0)
            ],
            "name_choices": ["slope", "aspect", "cosine_i", "sensor_zn", "sensor_az",
                             "solar_zn", "solar_az", "phase", "path_length", "utc_time"]
        },
        "neon_edge": {
            "params": [
                ("radius", int, 1)
            ]
        },
        "kernel_finite": {
            "params": []
        },
        "water": {
            "params": [
                ("band_1", int, 550),
                ("band_2", int, 850),
                ("threshold", float, 0.0)
            ]
        },
        "external": {
            "params": [
                ("file_path", str, ""),
                ("class", int, 1)
            ]
        },
        "band": {
            "params": [
                ("band", int, 650),
                ("min", float, 0.0),
                ("max", float, 1.0)
            ]
        }
    }

    def __init__(self, condition=None, parent=None):
        super().__init__(parent)
        self.param_widgets = {}  # 存储参数名对应的控件
        self.init_ui()
        if condition:
            self.set_condition(condition)

    def init_ui(self):
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 5)

        # 类型选择行
        type_layout = QHBoxLayout()
        self.type_combo = NoWheelComboBox()
        self.type_combo.addItems(list(self.MASK_TYPES.keys()))
        self.type_combo.currentTextChanged.connect(self.on_type_changed)
        type_layout.addWidget(QLabel("类型:"))
        type_layout.addWidget(self.type_combo)

        self.delete_btn = QPushButton("删除")
        self.delete_btn.setFixedWidth(60)
        type_layout.addStretch()
        type_layout.addWidget(self.delete_btn)
        layout.addLayout(type_layout)

        # 参数区域（动态切换）
        self.param_stack = QStackedWidget()
        layout.addWidget(self.param_stack)

        # 为每种类型创建一个参数面板
        self.param_panels = {}
        for mask_type, info in self.MASK_TYPES.items():
            panel = self.create_param_panel(mask_type, info["params"])
            self.param_panels[mask_type] = panel
            self.param_stack.addWidget(panel)

        self.on_type_changed(self.type_combo.currentText())

    def create_param_panel(self, mask_type, params):
        """创建参数输入面板，返回 QWidget"""
        widget = QWidget()
        layout = QFormLayout(widget)
        layout.setContentsMargins(10, 5, 10, 5)
        layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
        layout.setLabelAlignment(Qt.AlignLeft)
        layout.setFormAlignment(Qt.AlignLeft)

        for param_name, param_type, default_value in params:
            if param_type == int:
                if "band" in param_name or "radius" in param_name:
                    spin = NoWheelSpinBox()
                    spin.setRange(0, 3000)
                    spin.setValue(default_value)
                    param_widget = spin
                else:
                    spin = NoWheelSpinBox()
                    spin.setRange(-10000, 10000)
                    spin.setValue(default_value)
                    param_widget = spin
            elif param_type == float:
                dspin = NoWheelDoubleSpinBox()
                dspin.setRange(-1e6, 1e6)
                dspin.setDecimals(6)
                dspin.setValue(default_value)
                param_widget = dspin
            else:  # str
                if param_name == "name" and "name_choices" in self.MASK_TYPES[mask_type]:
                    combo = NoWheelComboBox()
                    combo.addItems(self.MASK_TYPES[mask_type]["name_choices"])
                    combo.setCurrentText(default_value)
                    param_widget = combo
                elif param_name == "method" and "method_choices" in self.MASK_TYPES[mask_type]:
                    combo = NoWheelComboBox()
                    combo.addItems(self.MASK_TYPES[mask_type]["method_choices"])
                    combo.setCurrentText(default_value)
                    param_widget = combo
                elif param_name == "file_path":
                    container = QWidget()
                    hbox = QHBoxLayout(container)
                    hbox.setContentsMargins(0, 0, 0, 0)
                    line_edit = QLineEdit(default_value)
                    btn = QPushButton("浏览")
                    btn.clicked.connect(lambda: self.browse_file(line_edit))
                    hbox.addWidget(line_edit)
                    hbox.addWidget(btn)
                    param_widget = container
                    # 存储 line_edit 以便获取值
                    setattr(param_widget, "line_edit", line_edit)
                else:
                    line = QLineEdit(str(default_value))
                    param_widget = line

            self.param_widgets[(mask_type, param_name)] = param_widget
            label = param_name.replace("_", " ").title()
            layout.addRow(f"{label}:", param_widget)

        return widget

    def browse_file(self, line_edit):
        file_path, _ = QFileDialog.getOpenFileName(self, "选择外部掩膜文件")
        if file_path:
            line_edit.setText(file_path)

    def on_type_changed(self, mask_type):
        """切换掩膜类型时显示对应的参数面板"""
        idx = self.param_stack.indexOf(self.param_panels[mask_type])
        if idx >= 0:
            self.param_stack.setCurrentIndex(idx)

    def get_condition(self):
        """返回当前条件的列表格式，如 ['ndi', {'band_1':550, ...}]"""
        mask_type = self.type_combo.currentText()
        params = {}
        for (mt, param_name), widget in self.param_widgets.items():
            if mt != mask_type:
                continue
            # 获取值
            if isinstance(widget, QSpinBox):
                value = widget.value()
            elif isinstance(widget, QDoubleSpinBox):
                value = widget.value()
            elif isinstance(widget, QComboBox):
                value = widget.currentText()
            elif hasattr(widget, "line_edit"):  # 文件路径容器
                value = widget.line_edit.text()
            elif isinstance(widget, QLineEdit):
                value = widget.text()
            else:
                continue
            # 特殊处理 external 的 class 参数（关键字 class 与 Python 冲突）
            if param_name == "class":
                param_name = "class"
            params[param_name] = value

        # 对于 kernel_finite 没有参数
        if mask_type == "kernel_finite":
            return [mask_type, {}]
        return [mask_type, params]

    def set_condition(self, condition):
        """从已有的条件（列表格式）填充界面"""
        if not isinstance(condition, list) or len(condition) < 2:
            return
        mask_type = condition[0]
        params = condition[1]
        if mask_type not in self.MASK_TYPES:
            return
        self.type_combo.setCurrentText(mask_type)
        # 设置参数值
        for param_name, value in params.items():
            # 处理关键字 class
            if param_name == "class":
                param_name = "class"
            widget = self.param_widgets.get((mask_type, param_name))
            if widget is None:
                continue
            if isinstance(widget, QSpinBox):
                widget.setValue(int(value))
            elif isinstance(widget, QDoubleSpinBox):
                widget.setValue(float(value))
            elif isinstance(widget, QComboBox):
                idx = widget.findText(str(value))
                if idx >= 0:
                    widget.setCurrentIndex(idx)
            elif hasattr(widget, "line_edit"):
                widget.line_edit.setText(str(value))
            elif isinstance(widget, QLineEdit):
                widget.setText(str(value))


class MaskListWidget(QWidget):
    """管理一组掩膜条件的列表，支持添加、删除、清空"""

    def __init__(self, parent=None):
        super().__init__(parent)
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(5)
        self.conditions = []  # 存储 MaskConditionEditor 控件

        # 添加按钮
        add_btn = QPushButton("+ 添加掩膜条件")
        add_btn.clicked.connect(self.add_condition)
        self.layout.addWidget(add_btn)

        # 容器用于存放条件编辑器
        self.conditions_container = QWidget()
        self.conditions_layout = QVBoxLayout(self.conditions_container)
        self.conditions_layout.setContentsMargins(0, 0, 0, 0)
        self.layout.addWidget(self.conditions_container)

    def add_condition(self, condition=None):
        editor = MaskConditionEditor(condition)
        editor.delete_btn.clicked.connect(lambda: self.remove_condition(editor))
        self.conditions.append(editor)
        self.conditions_layout.addWidget(editor)
        return editor

    def remove_condition(self, editor):
        if editor in self.conditions:
            self.conditions.remove(editor)
            editor.deleteLater()
            self.conditions_layout.removeWidget(editor)

    def clear(self):
        for editor in self.conditions[:]:
            self.remove_condition(editor)

    def get_conditions(self):
        return [editor.get_condition() for editor in self.conditions]

    def set_conditions(self, conditions_list):
        self.clear()
        for cond in conditions_list:
            self.add_condition(cond)


# ==================== 命令执行线程 ====================
class CommandThread(QThread):
    """后台执行命令行程序的线程"""
    output_updated = pyqtSignal(str)  # 输出更新信号
    progress_updated = pyqtSignal(int)  # 进度更新信号
    finished_signal = pyqtSignal(bool, str)  # 完成信号(成功/失败, 消息)

    def __init__(self, command, working_dir=None, env=None):
        super().__init__()
        self.command = command
        self.working_dir = working_dir
        self.env = env  # 环境变量字典
        self.process = None

    def run(self):
        try:
            # 合并环境变量
            run_env = os.environ.copy()
            if self.env:
                run_env.update(self.env)

            # 设置Python环境变量以确保UTF-8编码
            run_env['PYTHONIOENCODING'] = 'utf-8'

            self.process = subprocess.Popen(
                self.command,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                bufsize=1,
                cwd=self.working_dir,
                shell=True,
                env=run_env,
                encoding='utf-8',
                errors='replace'  # 无法解码的字符用替换
            )

            while True:
                output = self.process.stdout.readline()
                if output == '' and self.process.poll() is not None:
                    break
                if output:
                    self.output_updated.emit(output.strip())

            return_code = self.process.poll()
            if return_code == 0:
                self.finished_signal.emit(True, "处理完成！")
            else:
                self.finished_signal.emit(False, f"处理失败，退出代码：{return_code}")

        except Exception as e:
            self.finished_signal.emit(False, f"执行错误：{str(e)}")

    def stop(self):
        if self.process:
            self.process.terminate()


# ==================== 模块选择主窗口 ====================
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("BRDF 校正工具 - 高光谱图像处理套件")
        self.setMinimumSize(1000, 700)
        
        # 设置应用图标和样式
        self.setup_styles()
        
        # 创建主界面
        self.setup_main_interface()
        
    def setup_styles(self):
        """设置NVIDIA风格的样式"""
        self.setStyleSheet("""
            QMainWindow {
                background-color: #000000;
            }
            QWidget {
                background-color: #000000;
                color: #ffffff;
                font-family: 'Segoe UI', Arial, sans-serif;
                font-size: 14px;
            }
            QLabel {
                color: #ffffff;
                font-size: 14px;
            }
            QPushButton {
                background-color: transparent;
                color: #ffffff;
                border: 2px solid #76b900;
                border-radius: 8px;
                padding: 12px 24px;
                font-weight: bold;
                font-size: 16px;
                min-height: 40px;
            }
            QPushButton:hover {
                background-color: #76b900;
                color: #000000;
            }
            QPushButton:pressed {
                background-color: #5e9400;
                border-color: #5e9400;
            }
            QGroupBox {
                background-color: #1a1a1a;
                border: 2px solid #76b900;
                border-radius: 8px;
                margin-top: 20px;
                padding-top: 20px;
                font-weight: bold;
                font-size: 16px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 15px;
                padding: 5px 15px;
                color: #000000;
                background-color: #76b900;
                border-radius: 4px;
                font-weight: bold;
            }
        """)
        
    def setup_main_interface(self):
        """创建模块选择主界面"""
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        main_layout = QVBoxLayout(central_widget)
        main_layout.setContentsMargins(40, 40, 40, 40)
        main_layout.setSpacing(30)
        
        # 标题区域
        title_label = QLabel("BRDF 校正工具")
        title_label.setAlignment(Qt.AlignCenter)
        title_label.setStyleSheet("""
            font-size: 36px;
            font-weight: bold;
            color: #76b900;
            margin-bottom: 20px;
        """)
        main_layout.addWidget(title_label)
        
        subtitle_label = QLabel("高光谱图像双向反射分布函数校正 - 支持陆地植被和水体两种场景")
        subtitle_label.setAlignment(Qt.AlignCenter)
        subtitle_label.setStyleSheet("""
            font-size: 16px;
            color: #cccccc;
            margin-bottom: 40px;
        """)
        main_layout.addWidget(subtitle_label)
        
        # 模块选择区域
        modules_layout = QHBoxLayout()
        modules_layout.setSpacing(40)
        
        # 陆地植被BRDF模块
        land_group = self.create_module_card(
            title="陆地植被 BRDF 校正",
            subtitle="FlexBRDF算法",
            description="适用于陆地植被和地表的BRDF、地形和镜面反射校正\n支持AVIRIS、AVIRIS-NG等机载高光谱数据",
            features=[
                "✓ FlexBRDF 算法",
                "✓ 地形校正 (SCS, SCS+C, Cosine等)",
                "✓ 镜面反射校正",
                "✓ 多种掩膜条件",
                "✓ 并行处理支持"
            ],
            color="#76b900",
            button_text="进入陆地模块",
            callback=self.open_land_module
        )
        modules_layout.addWidget(land_group)
        
        # 水体BRDF模块
        water_group = self.create_module_card(
            title="水体 BRDF 校正",
            subtitle="Ocean BRDF Correction",
            description="专用于水体和海洋的BRDF校正\n支持多种BRDF模型和不确定性估算",
            features=[
                "✓ L11, M02, O25 BRDF模型",
                "✓ 不确定性估算",
                "✓ 水体掩膜自动识别",
                "✓ 分块处理支持",
                "✓ 多种输出格式"
            ],
            color="#0080ff",
            button_text="进入水体模块",
            callback=self.open_water_module
        )
        modules_layout.addWidget(water_group)
        
        main_layout.addLayout(modules_layout)
        main_layout.addStretch()
        
    def create_module_card(self, title, subtitle, description, features, color, button_text, callback):
        """创建模块卡片"""
        group = QGroupBox()
        group.setMinimumHeight(400)
        group.setStyleSheet(f"""
            QGroupBox {{
                background-color: #1a1a1a;
                border: 2px solid {color};
                border-radius: 12px;
                margin: 10px;
                padding: 20px;
            }}
        """)
        
        layout = QVBoxLayout(group)
        layout.setSpacing(20)
        
        # 标题
        title_label = QLabel(title)
        title_label.setAlignment(Qt.AlignCenter)
        title_label.setStyleSheet(f"""
            font-size: 24px;
            font-weight: bold;
            color: {color};
            margin-bottom: 5px;
        """)
        layout.addWidget(title_label)
        
        # 副标题
        subtitle_label = QLabel(subtitle)
        subtitle_label.setAlignment(Qt.AlignCenter)
        subtitle_label.setStyleSheet("""
            font-size: 14px;
            color: #aaaaaa;
            margin-bottom: 15px;
        """)
        layout.addWidget(subtitle_label)
        
        # 描述
        desc_label = QLabel(description)
        desc_label.setAlignment(Qt.AlignCenter)
        desc_label.setWordWrap(True)
        desc_label.setStyleSheet("""
            font-size: 13px;
            color: #dddddd;
            margin-bottom: 20px;
            line-height: 1.4;
        """)
        layout.addWidget(desc_label)
        
        # 功能特性列表
        features_label = QLabel("\n".join(features))
        features_label.setAlignment(Qt.AlignLeft)
        features_label.setStyleSheet("""
            font-size: 12px;
            color: #cccccc;
            margin-bottom: 20px;
            background-color: #2a2a2a;
            padding: 15px;
            border-radius: 6px;
        """)
        layout.addWidget(features_label)
        
        layout.addStretch()
        
        # 操作按钮
        button = QPushButton(button_text)
        button.setStyleSheet(f"""
            QPushButton {{
                background-color: transparent;
                color: {color};
                border: 2px solid {color};
                border-radius: 8px;
                padding: 12px 24px;
                font-weight: bold;
                font-size: 16px;
                min-height: 40px;
            }}
            QPushButton:hover {{
                background-color: {color};
                color: #000000;
            }}
            QPushButton:pressed {{
                background-color: {color};
                opacity: 0.8;
            }}
        """)
        button.clicked.connect(callback)
        layout.addWidget(button)
        
        return group
    
    def open_land_module(self):
        """打开陆地植被BRDF模块"""
        self.land_window = LandBRDFWindow()
        self.land_window.show()
        # 可选：隐藏主窗口 
        # self.hide()
        
    def open_water_module(self):
        """打开水体BRDF模块"""
        self.water_window = WaterBRDFWindow()
        self.water_window.show()
        # 可选：隐藏主窗口
        # self.hide()


# ==================== 陆地植被BRDF模块窗口 ====================
class LandBRDFWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("陆地植被 BRDF 校正 - FlexBRDF")
        self.setMinimumSize(1200, 800)
        self.resize(1400, 900)  # 设置默认大小

        # 存储匹配后的文件对列表
        self.file_pairs = []  # [(rfl_path, obs_path), ...]
        # 存储每个输入文件对应的辅助文件波段映射
        self.band_mappings = {}  # {rfl_path: {param_name: band_index}}
        
        # 命令执行相关
        self.command_thread = None
        
        # 设置样式和界面
        self.setup_land_styles()
        self.setup_land_interface()
        
    def setup_land_styles(self):
        """设置陆地植被BRDF模块的样式"""
        self.setStyleSheet("""
            QMainWindow {
                background-color: #000000;
            }
            QWidget {
                background-color: #000000;
                color: #ffffff;
                font-family: Arial, Helvetica, sans-serif;
                font-size: 14px;
            }
            QTabWidget::pane {
                border: 2px solid #76b900;
                border-radius: 2px;
                background-color: #000000;
                padding: 10px;
            }
            QTabBar::tab {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 2px solid #5e5e5e;
                border-radius: 2px;
                padding: 10px 20px;
                margin-right: 5px;
                font-weight: 700;
                font-size: 14px;
            }
            QTabBar::tab:selected {
                background-color: #76b900;
                color: #000000;
                border: 2px solid #76b900;
            }
            QTabBar::tab:hover:!selected {
                background-color: #3860be;
                border: 2px solid #3860be;
            }
            QPushButton {
                background-color: transparent;
                color: #ffffff;
                border: 2px solid #76b900;
                border-radius: 2px;
                padding: 8px 16px;
                font-weight: 700;
                font-size: 14px;
            }
            QPushButton:hover {
                background-color: #1eaedb;
                color: #ffffff;
            }
            QPushButton:pressed {
                background-color: #007fff;
                color: #ffffff;
                border: 1px solid #003eff;
            }
            QLineEdit {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #5e5e5e;
                border-radius: 2px;
                padding: 5px;
            }
            QLineEdit:focus {
                border: 1px solid #76b900;
            }
            QListWidget {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #5e5e5e;
                border-radius: 2px;
                padding: 5px;
            }
            QListWidget::item {
                padding: 5px;
            }
            QListWidget::item:selected {
                background-color: #76b900;
                color: #000000;
            }
            QListWidget::item:hover {
                background-color: #3860be;
            }
            QGroupBox {
                background-color: #1a1a1a;
                border: 1px solid #5e5e5e;
                border-radius: 2px;
                margin-top: 10px;
                padding-top: 10px;
                font-weight: 700;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 5px;
                color: #ffffff;
            }
            QLabel {
                color: #ffffff;
                font-size: 14px;
            }
            QScrollArea {
                border: none;
                background-color: #000000;
            }
            QTextEdit {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #5e5e5e;
                border-radius: 2px;
                font-family: 'Consolas', 'Monaco', monospace;
                font-size: 12px;
            }
            QProgressBar {
                background-color: #1a1a1a;
                border: 1px solid #5e5e5e;
                border-radius: 2px;
                text-align: center;
                color: #ffffff;
            }
            QProgressBar::chunk {
                background-color: #76b900;
                border-radius: 2px;
            }
        """)

    def setup_land_interface(self):
        """创建陆地植被BRDF模块界面"""
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        main_layout.setSpacing(15)
        main_layout.setContentsMargins(20, 20, 20, 20)

        # 创建分割器：左侧配置，右侧日志
        splitter = QSplitter(Qt.Horizontal)
        
        # 左侧：配置选项卡
        left_widget = QWidget()
        left_layout = QVBoxLayout(left_widget)
        
        tabs = QTabWidget()
        tabs.setDocumentMode(True)
        # tabs.setMaximumHeight(500)  # 限制标签页高度，确保按钮可见
        left_layout.addWidget(tabs, 1)  # stretch factor = 1

        # 1. 文件与波段映射页
        self.file_tab = QWidget()
        tabs.addTab(self.file_tab, "文件与波段映射")
        self.setup_file_tab()

        # 2. 校正参数页
        self.corr_tab = QWidget()
        tabs.addTab(self.corr_tab, "校正参数")
        self.setup_corr_tab()

        # 3. 输出设置页
        self.output_tab = QWidget()
        tabs.addTab(self.output_tab, "输出设置")
        self.setup_output_tab()

        # 底部按钮区域 - 使用独立的分组框使其更显眼
        btn_group = QGroupBox("操作")
        btn_group.setMinimumHeight(90)  # 确保按钮组有足够高度
        btn_group.setStyleSheet("""
            QGroupBox {
                background-color: #1a1a1a;
                border: 2px solid #76b900;
                border-radius: 4px;
                margin-top: 5px;
                padding: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 2px 10px;
                color: #76b900;
                font-weight: bold;
                font-size: 13px;
            }
        """)
        btn_layout = QHBoxLayout(btn_group)
        btn_layout.setSpacing(15)
        
        load_btn = QPushButton("📂 加载 JSON 配置")
        load_btn.setMinimumHeight(40)
        load_btn.clicked.connect(self.load_config)
        
        save_btn = QPushButton("💾 保存 JSON 配置")
        save_btn.setMinimumHeight(40)
        save_btn.clicked.connect(self.save_config)
        
        generate_run_btn = QPushButton("▶ 生成配置并运行")
        generate_run_btn.setMinimumHeight(55)
        generate_run_btn.setMinimumWidth(220)
        generate_run_btn.setStyleSheet("""
            QPushButton {
                background-color: #76b900;
                color: #000000;
                border: 3px solid #76b900;
                border-radius: 8px;
                font-weight: bold;
                font-size: 16px;
                padding: 15px 30px;
            }
            QPushButton:hover {
                background-color: #8fce00;
                border-color: #8fce00;
            }
            QPushButton:pressed {
                background-color: #5e9400;
                border-color: #5e9400;
            }
        """)
        generate_run_btn.clicked.connect(self.generate_and_run)
        
        btn_layout.addWidget(load_btn)
        btn_layout.addWidget(save_btn)
        btn_layout.addStretch()
        btn_layout.addWidget(generate_run_btn)
        left_layout.addWidget(btn_group, 0, Qt.AlignBottom)  # 固定高度，底部对齐
        
        # 右侧：日志和进度显示
        right_widget = QWidget()
        right_layout = QVBoxLayout(right_widget)
        
        log_label = QLabel("执行日志")
        log_label.setStyleSheet("font-weight: bold; color: #76b900; font-size: 16px;")
        right_layout.addWidget(log_label)
        
        self.log_text = QTextEdit()
        self.log_text.setMaximumWidth(400)
        self.log_text.setPlainText("等待执行命令...\n")
        right_layout.addWidget(self.log_text)
        
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        right_layout.addWidget(self.progress_bar)
        
        # 添加到分割器
        splitter.addWidget(left_widget)
        splitter.addWidget(right_widget)
        splitter.setStretchFactor(0, 2)  # 左侧占2/3
        splitter.setStretchFactor(1, 1)  # 右侧占1/3
        
        main_layout.addWidget(splitter)

    def generate_and_run(self):
        """生成JSON配置并运行FlexBRDF"""
        if not self.file_pairs:
            QMessageBox.warning(self, "警告", "没有有效的文件对，请先匹配文件。")
            return
            
        # 生成JSON配置
        config = self.build_config()
        
        # 保存临时JSON文件
        temp_json_path = os.path.join(os.path.dirname(__file__), "temp_flexbrdf_config.json")
        try:
            with open(temp_json_path, 'w', encoding='utf-8') as f:
                json.dump(config, f, indent=2, ensure_ascii=False)
        except Exception as e:
            QMessageBox.critical(self, "错误", f"保存配置文件失败: {e}")
            return
        
        # 构建命令
        flexbrdf_path = os.path.join(os.path.dirname(__file__), "..", "Flexbrdf", "Flex_brdf.py")
        flexbrdf_path = os.path.abspath(flexbrdf_path)
        flexbrdf_dir = os.path.dirname(flexbrdf_path)
        
        if not os.path.exists(flexbrdf_path):
            QMessageBox.critical(self, "错误", f"找不到 Flex_brdf.py 文件:\n{flexbrdf_path}")
            return
        
        # 使用当前Python解释器
        python_exe = sys.executable
        command = f'"{python_exe}" "{flexbrdf_path}" "{temp_json_path}"'
        
        # 显示进度条和日志
        self.progress_bar.setVisible(True)
        self.progress_bar.setRange(0, 0)  # 不确定进度
        self.log_text.clear()
        self.log_text.append("开始执行 FlexBRDF 校正...")
        self.log_text.append(f"Python: {python_exe}")
        self.log_text.append(f"命令: {command}\n")
        
        # 启动命令执行线程
        self.command_thread = CommandThread(command, flexbrdf_dir)
        self.command_thread.output_updated.connect(self.append_log)
        self.command_thread.finished_signal.connect(self.command_finished)
        self.command_thread.start()
        
    def append_log(self, text):
        """添加日志文本"""
        self.log_text.append(text)
        # 自动滚动到底部
        cursor = self.log_text.textCursor()
        cursor.movePosition(cursor.End)
        self.log_text.setTextCursor(cursor)
        
    def command_finished(self, success, message):
        """命令执行完成"""
        self.progress_bar.setVisible(False)
        self.log_text.append(f"\n{message}")
        
        if success:
            QMessageBox.information(self, "成功", "FlexBRDF 校正完成！")
        else:
            QMessageBox.warning(self, "警告", f"FlexBRDF 校正失败:\n{message}")
    
    def build_config(self):
        """构建FlexBRDF配置字典"""
        config = {}
        config["input_files"] = [rfl for rfl, _ in self.file_pairs]
        config["file_type"] = "envi"
        config["bad_bands"] = []
        config["num_cpus"] = self.num_cpus_spin.value()

        anc_files_dict = {}
        for rfl_path, obs_path in self.file_pairs:
            mapping = self.band_mappings.get(rfl_path, {})
            anc_entry = {}
            for param, spin in self.param_spinboxes.items():
                band_idx = mapping.get(param, spin.value())
                anc_entry[param] = [obs_path, band_idx]
            anc_files_dict[rfl_path] = anc_entry
        config["anc_files"] = anc_files_dict

        corrections = []
        if self.corr_topo_cb.isChecked():
            corrections.append("topo")
        if self.corr_brdf_cb.isChecked():
            corrections.append("brdf")
        if self.corr_glint_cb.isChecked():
            corrections.append("glint")
        config["corrections"] = corrections

        config["topo"] = {
            "type": self.topo_type.currentText(),
            "calc_mask": self.topo_calc_mask_list.get_conditions(),
            "apply_mask": self.topo_apply_mask_list.get_conditions(),
            "c_fit_type": self.c_fit_type.currentText()
        }

        config["brdf"] = {
            "type": self.brdf_type.currentText(),
            "grouped": self.brdf_grouped.isChecked(),
            "geometric": self.geometric_kernel.currentText(),
            "volume": self.volume_kernel.currentText(),
            "b/r": self.br_b_ratio.value(),
            "h/b": self.hb_ratio.value(),
            "sample_perc": self.sample_perc.value(),
            "interp_kind": self.interp_kind.currentText(),
            "calc_mask": self.brdf_calc_mask_list.get_conditions(),
            "apply_mask": self.brdf_apply_mask_list.get_conditions(),
            "bin_type": self.bin_type.currentText(),
            "num_bins": self.num_bins.value(),
            "ndvi_bin_min": self.ndvi_bin_min.value(),
            "ndvi_bin_max": self.ndvi_bin_max.value(),
            "ndvi_perc_min": self.ndvi_perc_min.value(),
            "ndvi_perc_max": self.ndvi_perc_max.value(),
            "solar_zn_type": self.solar_zn_type.currentText()
        }

        config["glint"] = {
            "type": self.glint_type.currentText(),
            "correction_wave": self.correction_wave.value(),
            "apply_mask": self.glint_apply_mask_list.get_conditions()
        }

        subset_waves = []
        if self.subset_waves_edit.text().strip():
            try:
                subset_waves = [int(x.strip()) for x in self.subset_waves_edit.text().split(",") if x.strip()]
            except:
                pass
        config["export"] = {
            "coeffs": self.coeffs_cb.isChecked(),
            "image": self.image_cb.isChecked(),
            "masks": self.masks_cb.isChecked(),
            "subset_waves": subset_waves,
            "output_dir": self.output_dir_edit.text(),
            "suffix": self.suffix_edit.text()
        }
        config["resample"] = self.resample_cb.isChecked()
        
        return config

    # ==================== 文件与波段映射页 ====================
    def setup_file_tab(self):
        # 创建滚动区域作为主布局
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        scroll.setStyleSheet("""
            QScrollArea {
                border: none;
                background-color: #000000;
            }
            QScrollBar:vertical {
                background-color: #1a1a1a;
                width: 12px;
                border-radius: 6px;
            }
            QScrollBar::handle:vertical {
                background-color: #5e5e5e;
                border-radius: 6px;
                min-height: 20px;
            }
            QScrollBar::handle:vertical:hover {
                background-color: #76b900;
            }
            QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
                height: 0px;
            }
            QScrollBar:horizontal {
                background-color: #1a1a1a;
                height: 12px;
                border-radius: 6px;
            }
            QScrollBar::handle:horizontal {
                background-color: #5e5e5e;
                border-radius: 6px;
                min-width: 20px;
            }
            QScrollBar::handle:horizontal:hover {
                background-color: #76b900;
            }
            QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {
                width: 0px;
            }
        """)
        
        # 创建内容容器
        content_widget = QWidget()
        layout = QVBoxLayout(content_widget)
        layout.setSpacing(15)
        layout.setContentsMargins(15, 15, 15, 15)
        
        # ===== 模式选择 =====
        mode_group = QGroupBox("文件选择模式")
        mode_group.setStyleSheet("""
            QGroupBox {
                background-color: #1a1a1a;
                border: 2px solid #76b900;
                border-radius: 2px;
                color: #ffffff;
                font-weight: 700;
                font-size: 14px;
                padding: 10px;
                margin-top: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 5px 10px;
                color: #000000;
                background-color: #76b900;
                border-radius: 2px;
                font-weight: 700;
            }
            QRadioButton {
                color: #ffffff;
                font-size: 14px;
                spacing: 8px;
            }
            QRadioButton::indicator {
                width: 18px;
                height: 18px;
                border: 2px solid #76b900;
                border-radius: 9px;
                background-color: transparent;
            }
            QRadioButton::indicator:checked {
                background-color: #76b900;
            }
        """)
        mode_layout = QHBoxLayout()
        self.mode_auto_radio = QRadioButton("自动匹配文件夹")
        self.mode_auto_radio.setChecked(True)
        self.mode_auto_radio.toggled.connect(self.on_mode_changed)
        self.mode_manual_radio = QRadioButton("手动指定文件对")
        self.mode_manual_radio.setChecked(False)
        self.mode_manual_radio.toggled.connect(self.on_mode_changed)
        mode_layout.addWidget(self.mode_auto_radio)
        mode_layout.addWidget(self.mode_manual_radio)
        mode_layout.addStretch()
        mode_group.setLayout(mode_layout)
        layout.addWidget(mode_group)
        
        # 提示标签
        hint_label = QLabel("提示：自动匹配会从文件夹批量导入文件对，手动模式可逐个指定。文件对列表中会标记 [自动] 或 [手动]。")
        hint_label.setStyleSheet("color: #aaaaaa; font-size: 11px; margin-top: 5px;")
        hint_label.setWordWrap(True)
        layout.addWidget(hint_label)

        # ===== 自动匹配区域 =====
        self.auto_match_widget = QWidget()
        auto_layout = QVBoxLayout(self.auto_match_widget)
        auto_layout.setContentsMargins(0, 0, 0, 0)
        auto_layout.setSpacing(10)

        # 文件夹设置组 - 使用 NVIDIA 绿色主题
        folder_group = QGroupBox("文件夹设置 (Folder Settings)")
        folder_group.setStyleSheet("""
            QGroupBox {
                background-color: #1a1a1a;
                border: 2px solid #76b900;
                border-radius: 2px;
                color: #ffffff;
                font-weight: 700;
                font-size: 14px;
                padding: 10px;
                margin-top: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 5px 10px;
                color: #000000;
                background-color: #76b900;
                border-radius: 2px;
                font-weight: 700;
            }
            QLabel {
                color: #ffffff;
                font-size: 14px;
                font-weight: 700;
            }
        """)
        folder_layout = QFormLayout()
        folder_layout.setVerticalSpacing(10)
        folder_layout.setHorizontalSpacing(10)
        folder_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
        folder_layout.setLabelAlignment(Qt.AlignLeft)
        folder_layout.setFormAlignment(Qt.AlignLeft)
        self.input_folder_edit = QLineEdit()
        self.input_folder_edit.setPlaceholderText("选择反射率数据文件夹...")
        self.anc_folder_edit = QLineEdit()
        self.anc_folder_edit.setPlaceholderText("选择观测几何数据文件夹...")
        btn_input = QPushButton("浏览...")
        btn_anc = QPushButton("浏览...")
        btn_input.clicked.connect(lambda: self.select_folder(self.input_folder_edit))
        btn_anc.clicked.connect(lambda: self.select_folder(self.anc_folder_edit))
        folder_layout.addRow("输入文件夹 (反射率):", self._horizontal_widget(self.input_folder_edit, btn_input))
        folder_layout.addRow("辅助文件夹 (观测几何):", self._horizontal_widget(self.anc_folder_edit, btn_anc))
        folder_group.setLayout(folder_layout)
        auto_layout.addWidget(folder_group)

        # 匹配按钮
        match_btn = QPushButton("自动匹配文件对 (Auto Match File Pairs)")
        match_btn.setStyleSheet("""
            QPushButton {
                background-color: transparent;
                color: #ffffff;
                border: 2px solid #76b900;
                border-radius: 2px;
                padding: 10px 20px;
                font-weight: 700;
                font-size: 14px;
                margin-top: 10px;
            }
            QPushButton:hover {
                background-color: #1eaedb;
            }
        """)
        match_btn.clicked.connect(self.match_files)
        auto_layout.addWidget(match_btn)
        
        layout.addWidget(self.auto_match_widget)

        # ===== 手动指定区域 =====
        self.manual_widget = QWidget()
        manual_layout = QVBoxLayout(self.manual_widget)
        manual_layout.setContentsMargins(0, 0, 0, 0)
        manual_layout.setSpacing(10)

        manual_group = QGroupBox("手动添加文件对")
        manual_group.setStyleSheet("""
            QGroupBox {
                background-color: #1a1a1a;
                border: 2px solid #0080ff;
                border-radius: 2px;
                color: #ffffff;
                font-weight: 700;
                font-size: 14px;
                padding: 10px;
                margin-top: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 5px 10px;
                color: #ffffff;
                background-color: #0080ff;
                border-radius: 2px;
                font-weight: 700;
            }
            QLabel {
                color: #ffffff;
                font-size: 14px;
                font-weight: 700;
            }
        """)
        manual_form = QFormLayout()
        manual_form.setVerticalSpacing(10)
        manual_form.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
        manual_form.setLabelAlignment(Qt.AlignLeft)
        manual_form.setFormAlignment(Qt.AlignLeft)
        
        self.manual_rfl_edit = QLineEdit()
        self.manual_rfl_edit.setPlaceholderText("选择反射率文件...")
        btn_rfl = QPushButton("浏览...")
        btn_rfl.clicked.connect(self.select_rfl_file)
        manual_form.addRow("反射率文件:", self._horizontal_widget(self.manual_rfl_edit, btn_rfl))
        
        self.manual_obs_edit = QLineEdit()
        self.manual_obs_edit.setPlaceholderText("选择观测几何文件...")
        btn_obs = QPushButton("浏览...")
        btn_obs.clicked.connect(self.select_obs_file)
        manual_form.addRow("观测几何文件:", self._horizontal_widget(self.manual_obs_edit, btn_obs))
        
        manual_group.setLayout(manual_form)
        manual_layout.addWidget(manual_group)
        
        # 添加文件对按钮
        add_pair_btn = QPushButton("添加文件对 (Add File Pair)")
        add_pair_btn.setStyleSheet("""
            QPushButton {
                background-color: transparent;
                color: #ffffff;
                border: 2px solid #0080ff;
                border-radius: 2px;
                padding: 10px 20px;
                font-weight: 700;
                font-size: 14px;
                margin-top: 5px;
            }
            QPushButton:hover {
                background-color: #1eaedb;
            }
        """)
        add_pair_btn.clicked.connect(self.add_manual_file_pair)
        manual_layout.addWidget(add_pair_btn)
        
        self.manual_widget.setVisible(False)  # 默认隐藏
        layout.addWidget(self.manual_widget)

        # ===== 文件对列表 =====
        list_group = QGroupBox("文件对列表 (点击编辑波段映射)")
        list_group.setStyleSheet("""
            QGroupBox {
                background-color: #1a1a1a;
                border: 1px solid #5e5e5e;
                border-radius: 2px;
                color: #ffffff;
                font-weight: 700;
                font-size: 14px;
                padding: 10px;
                margin-top: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 5px;
                color: #ffffff;
            }
        """)
        list_layout = QVBoxLayout(list_group)
        
        # 文件列表（带滚动区域）
        list_scroll = QScrollArea()
        list_scroll.setWidgetResizable(True)
        list_scroll.setMaximumHeight(200)  # 限制最大高度，避免挤压
        list_scroll.setStyleSheet("""
            QScrollArea {
                border: none;
                background-color: #1a1a1a;
            }
            QScrollBar:vertical {
                background-color: #1a1a1a;
                width: 12px;
                border-radius: 6px;
            }
            QScrollBar::handle:vertical {
                background-color: #5e5e5e;
                border-radius: 6px;
                min-height: 20px;
            }
            QScrollBar::handle:vertical:hover {
                background-color: #76b900;
            }
            QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
                height: 0px;
            }
        """)
        
        list_container = QWidget()
        list_container_layout = QVBoxLayout(list_container)
        list_container_layout.setContentsMargins(0, 0, 0, 0)
        list_container_layout.setSpacing(0)
        
        self.file_list_widget = QListWidget()
        self.file_list_widget.setSelectionMode(QListWidget.SingleSelection)
        self.file_list_widget.itemClicked.connect(self.on_file_selected)
        self.file_list_widget.setMaximumHeight(400)  # 内部最大高度
        self.file_list_widget.setStyleSheet("""
            QListWidget {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #5e5e5e;
                border-radius: 2px;
                padding: 5px;
            }
            QListWidget::item {
                padding: 8px;
                border-bottom: 1px solid #5e5e5e;
            }
            QListWidget::item:selected {
                background-color: #76b900;
                color: #000000;
            }
            QListWidget::item:hover {
                background-color: #3860be;
            }
        """)
        list_container_layout.addWidget(self.file_list_widget)
        list_scroll.setWidget(list_container)
        list_layout.addWidget(list_scroll)
        
        # 删除按钮
        delete_btn = QPushButton("删除选中文件对 (Delete Selected)")
        delete_btn.setStyleSheet("""
            QPushButton {
                background-color: transparent;
                color: #ff4444;
                border: 2px solid #ff4444;
                border-radius: 2px;
                padding: 8px 16px;
                font-weight: 700;
                font-size: 13px;
            }
            QPushButton:hover {
                background-color: #ff4444;
                color: #000000;
            }
        """)
        delete_btn.clicked.connect(self.delete_selected_file_pair)
        list_layout.addWidget(delete_btn)
        
        layout.addWidget(list_group)

        # 波段映射组 - 使用蓝色主题
        mapping_group = QGroupBox("当前选中文件的辅助文件波段索引 (0-based)")
        mapping_group.setStyleSheet("""
            QGroupBox {
                background-color: #1a1a1a;
                border: 2px solid #0046a4;
                border-radius: 2px;
                color: #ffffff;
                font-weight: 700;
                font-size: 14px;
                padding: 10px;
                margin-top: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 5px 10px;
                color: #ffffff;
                background-color: #0046a4;
                border-radius: 2px;
                font-weight: 700;
            }
            QLabel {
                color: #ffffff;
                font-size: 13px;
                font-weight: 700;
            }
            QSpinBox {
                background-color: #000000;
                color: #ffffff;
                border: 1px solid #0046a4;
                border-radius: 2px;
                padding: 3px;
                min-width: 80px;
            }
        """)
        mapping_layout = QGridLayout()
        mapping_layout.setVerticalSpacing(10)
        mapping_layout.setHorizontalSpacing(15)
        self.param_spinboxes = {}
        
        # 参数名称映射：内部名称 -> 显示名称
        param_names_display = [
            ("path_length", "路径长度 (path_length)"),
            ("sensor_az", "传感器方位角 (sensor_az)"),
            ("sensor_zn", "传感器天顶角 (sensor_zn)"),
            ("solar_az", "太阳方位角 (solar_az)"),
            ("solar_zn", "太阳天顶角 (solar_zn)"),
            ("phase", "相位角 (phase)"),
            ("slope", "坡度 (slope)"),
            ("aspect", "坡向 (aspect)"),
            ("cosine_i", "入射角余弦 (cosine_i)"),
            ("utc_time", "UTC时间 (utc_time)")
        ]
        
        # 使用两列网格布局
        for i, (name, display_name) in enumerate(param_names_display):
            row = i // 2
            col = (i % 2) * 2  # 0, 2, 4... for labels
            
            label = QLabel(f"{display_name}:")
            label.setStyleSheet("color: #ffffff; font-size: 12px; min-width: 180px;")
            mapping_layout.addWidget(label, row, col)
            
            spin = NoWheelSpinBox()
            spin.setRange(0, 1000)
            spin.setValue(0)
            spin.setFixedWidth(80)
            spin.setStyleSheet("""
                QSpinBox {
                    background-color: #000000;
                    color: #ffffff;
                    border: 1px solid #0046a4;
                    border-radius: 2px;
                    padding: 3px;
                }
            """)
            self.param_spinboxes[name] = spin
            mapping_layout.addWidget(spin, row, col + 1)
        
        mapping_group.setLayout(mapping_layout)
        layout.addWidget(mapping_group)

        self.current_rfl_path = None
        
        # 设置滚动区域的内容
        scroll.setWidget(content_widget)
        
        # 将滚动区域添加到文件标签页
        tab_layout = QVBoxLayout(self.file_tab)
        tab_layout.setContentsMargins(0, 0, 0, 0)
        tab_layout.addWidget(scroll)

    def _horizontal_widget(self, line_edit, button):
        w = QWidget()
        hbox = QHBoxLayout(w)
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.setSpacing(10)
        hbox.addWidget(line_edit, 1)
        hbox.addWidget(button)
        return w

    def select_folder(self, line_edit):
        folder = QFileDialog.getExistingDirectory(self, "选择文件夹")
        if folder:
            line_edit.setText(folder)

    def match_files(self):
        input_dir = self.input_folder_edit.text()
        anc_dir = self.anc_folder_edit.text()
        if not input_dir or not anc_dir:
            QMessageBox.warning(self, "警告", "请先选择输入文件夹和辅助文件夹")
            return

        input_files = []
        for f in os.listdir(input_dir):
            if f.endswith("_rfl") or (os.path.isdir(os.path.join(input_dir, f)) and f.endswith("_rfl")):
                input_files.append(os.path.join(input_dir, f))

        anc_files = {}
        for f in os.listdir(anc_dir):
            if f.endswith("_obs"):
                anc_files[f[:-4]] = os.path.join(anc_dir, f)

        self.file_pairs = []
        self.file_list_widget.clear()
        for rfl_path in input_files:
            basename = os.path.basename(rfl_path)
            key = basename[:-4] if basename.endswith("_rfl") else basename
            if key in anc_files:
                self.file_pairs.append((rfl_path, anc_files[key]))
                self.file_list_widget.addItem(f"[自动] {basename}  ->  {os.path.basename(anc_files[key])}")
            else:
                self.file_list_widget.addItem(f"[自动] {basename}  ->  (未找到匹配的 obs 文件)")

        if self.file_pairs:
            self.file_list_widget.setCurrentRow(0)
            self.on_file_selected(self.file_list_widget.item(0))

    def on_mode_changed(self):
        """切换文件选择模式 - 互斥单选"""
        auto_mode = self.mode_auto_radio.isChecked()
        
        # 显示/隐藏对应区域
        self.auto_match_widget.setVisible(auto_mode)
        self.manual_widget.setVisible(not auto_mode)

    def select_rfl_file(self):
        """手动选择反射率文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, 
            "选择反射率文件", 
            "", 
            "ENVI Files (*.dat *.img *.hdr);;All Files (*)"
        )
        if file_path:
            self.manual_rfl_edit.setText(file_path)

    def select_obs_file(self):
        """手动选择观测几何文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, 
            "选择观测几何文件", 
            "", 
            "ENVI Files (*.dat *.img *.hdr *.bip *.bsq *.bil);;All Files (*)"
        )
        if file_path:
            self.manual_obs_edit.setText(file_path)

    def add_manual_file_pair(self):
        """添加手动指定的文件对"""
        rfl_path = self.manual_rfl_edit.text().strip()
        obs_path = self.manual_obs_edit.text().strip()
        
        # 验证输入
        if not rfl_path:
            QMessageBox.warning(self, "警告", "请选择反射率文件")
            return
        if not obs_path:
            QMessageBox.warning(self, "警告", "请选择观测几何文件")
            return
        
        # 检查文件是否存在
        if not os.path.exists(rfl_path):
            QMessageBox.warning(self, "警告", f"反射率文件不存在：\n{rfl_path}")
            return
        if not os.path.exists(obs_path):
            QMessageBox.warning(self, "警告", f"观测几何文件不存在：\n{obs_path}")
            return
        
        # 检查是否已存在相同的文件对
        for existing_rfl, existing_obs in self.file_pairs:
            if existing_rfl == rfl_path and existing_obs == obs_path:
                QMessageBox.information(self, "提示", "该文件对已存在")
                return
        
        # 添加文件对
        self.file_pairs.append((rfl_path, obs_path))
        rfl_basename = os.path.basename(rfl_path)
        obs_basename = os.path.basename(obs_path)
        self.file_list_widget.addItem(f"[手动] {rfl_basename}  ->  {obs_basename}")
        
        # 为新文件初始化默认波段映射
        if rfl_path not in self.band_mappings:
            self.band_mappings[rfl_path] = {
                "path_length": 0, "sensor_az": 9, "sensor_zn": 8, 
                "solar_az": 7, "solar_zn": 6, "phase": 0,
                "slope": 0, "aspect": 0, "cosine_i": 0, "utc_time": 0
            }
        
        # 选中新添加的文件对
        last_idx = len(self.file_pairs) - 1
        self.file_list_widget.setCurrentRow(last_idx)
        self.on_file_selected(self.file_list_widget.item(last_idx))
        
        # 清空输入框
        self.manual_rfl_edit.clear()
        self.manual_obs_edit.clear()
        
        QMessageBox.information(self, "成功", "文件对已添加")

    def delete_selected_file_pair(self):
        """删除选中的文件对"""
        idx = self.file_list_widget.currentRow()
        if idx < 0 or idx >= len(self.file_pairs):
            QMessageBox.warning(self, "警告", "请先选择一个文件对")
            return
        
        rfl_path, obs_path = self.file_pairs[idx]
        rfl_name = os.path.basename(rfl_path)
        
        # 确认删除
        reply = QMessageBox.question(
            self, "确认删除", 
            f"确定要删除文件对吗？\n\n{rfl_name}",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            # 从列表和数据结构中删除
            self.file_pairs.pop(idx)
            self.file_list_widget.takeItem(idx)
            
            # 清除对应的波段映射
            if rfl_path in self.band_mappings:
                del self.band_mappings[rfl_path]
            
            # 清除当前选中
            self.current_rfl_path = None
            for spin in self.param_spinboxes.values():
                spin.setValue(0)
            
            # 如果有剩余文件对，选中第一个
            if self.file_pairs:
                self.file_list_widget.setCurrentRow(0)
                self.on_file_selected(self.file_list_widget.item(0))
            
            QMessageBox.information(self, "成功", "文件对已删除")

    def on_file_selected(self, item):
        idx = self.file_list_widget.row(item)
        if idx >= len(self.file_pairs):
            return
        rfl_path, obs_path = self.file_pairs[idx]
        self.current_rfl_path = rfl_path
        
        # 更新波段映射spinbox值
        if rfl_path in self.band_mappings:
            mapping = self.band_mappings[rfl_path]
            for param, spin in self.param_spinboxes.items():
                value = mapping.get(param, 0)
                spin.setValue(value)
        else:
            for spin in self.param_spinboxes.values():
                spin.setValue(0)
        
        # 强制刷新UI
        QApplication.processEvents()

        # 重新连接信号
        for param, spin in self.param_spinboxes.items():
            try:
                spin.valueChanged.disconnect()
            except:
                pass
            spin.valueChanged.connect(lambda val, p=param: self.update_band_mapping(p, val))

    def update_band_mapping(self, param, value):
        if self.current_rfl_path is None:
            return
        if self.current_rfl_path not in self.band_mappings:
            self.band_mappings[self.current_rfl_path] = {}
        self.band_mappings[self.current_rfl_path][param] = value

    # ==================== 校正参数页 ====================
    def setup_corr_tab(self):
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        container = QWidget()
        scroll.setWidget(container)
        layout = QVBoxLayout(container)
        layout.setSpacing(20)

        # NVIDIA 设计风格样式表
        # Topo 组 - 蓝色系 (#0046a4)
        # BRDF 组 - NVIDIA 绿色 (#76b900)
        # Glint 组 - 橙色系 (#df6500)

        # 1. Corrections 选择
        corr_group = QGroupBox("需要执行的校正")
        corr_group.setStyleSheet("""
            QGroupBox {
                background-color: #1a1a1a;
                border: 2px solid #76b900;
                border-radius: 2px;
                color: #ffffff;
                font-weight: 700;
                font-size: 14px;
                padding: 10px;
                margin-top: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 5px;
                color: #76b900;
            }
            QCheckBox {
                color: #ffffff;
                font-size: 14px;
                font-weight: 700;
                spacing: 8px;
            }
            QCheckBox::indicator {
                width: 18px;
                height: 18px;
                border: 2px solid #76b900;
                border-radius: 2px;
                background-color: transparent;
            }
            QCheckBox::indicator:checked {
                background-color: #76b900;
            }
        """)
        corr_layout = QHBoxLayout()
        self.corr_topo_cb = QCheckBox("Topo (地形校正)")
        self.corr_topo_cb.stateChanged.connect(self.on_correction_changed)
        self.corr_brdf_cb = QCheckBox("BRDF (双向反射分布函数)")
        self.corr_brdf_cb.stateChanged.connect(self.on_correction_changed)
        self.corr_glint_cb = QCheckBox("Glint (耀斑校正)")
        self.corr_glint_cb.stateChanged.connect(self.on_correction_changed)
        corr_layout.addWidget(self.corr_topo_cb)
        corr_layout.addWidget(self.corr_brdf_cb)
        corr_layout.addWidget(self.corr_glint_cb)
        corr_group.setLayout(corr_layout)
        layout.addWidget(corr_group)

        # 2. Topo 参数 - 蓝色主题 (#0046a4)
        self.topo_group = QGroupBox("地形校正 (Topo Correction)")
        self.topo_group.setVisible(False)  # 默认隐藏
        self.topo_group.setStyleSheet("""
            QGroupBox {
                background-color: #1a1a1a;
                border: 2px solid #0046a4;
                border-radius: 2px;
                color: #ffffff;
                font-weight: 700;
                font-size: 14px;
                padding: 10px;
                margin-top: 15px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 5px 10px;
                color: #ffffff;
                background-color: #0046a4;
                border-radius: 2px;
                font-weight: 700;
                font-size: 14px;
            }
            QLabel {
                color: #ffffff;
                font-size: 14px;
                font-weight: 700;
            }
            QComboBox {
                background-color: #000000;
                color: #ffffff;
                border: 1px solid #0046a4;
                border-radius: 2px;
                padding: 5px;
                min-width: 150px;
            }
            QComboBox::drop-down {
                border: none;
                width: 25px;
            }
            QComboBox::down-arrow {
                image: none;
                border-left: 5px solid transparent;
                border-right: 5px solid transparent;
                border-top: 6px solid #0046a4;
                margin-right: 8px;
            }
            QComboBox QAbstractItemView {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #0046a4;
                selection-background-color: #0046a4;
            }
        """)
        topo_layout = QVBoxLayout()
        topo_layout.setSpacing(10)
        # type
        type_layout = QHBoxLayout()
        type_layout.setSpacing(10)
        type_label = QLabel("类型 (type):")
        type_layout.addWidget(type_label)
        self.topo_type = NoWheelComboBox()
        self.topo_type.addItems(["mod_minneart", "scs+c", "cosine", "c", "scs"])
        self.topo_type.setMinimumWidth(250)
        type_layout.addWidget(self.topo_type)
        type_layout.addStretch()
        topo_layout.addLayout(type_layout)
        # c_fit_type
        fit_layout = QHBoxLayout()
        fit_layout.setSpacing(10)
        fit_label = QLabel("拟合类型 (c_fit_type):")
        fit_layout.addWidget(fit_label)
        self.c_fit_type = NoWheelComboBox()
        self.c_fit_type.addItems(["nnls", "ols", "wls"])
        self.c_fit_type.setMinimumWidth(250)
        fit_layout.addWidget(self.c_fit_type)
        fit_layout.addStretch()
        topo_layout.addLayout(fit_layout)
        # 掩膜列表
        calc_mask_label = QLabel("计算掩膜条件 (calc_mask - 取交集):")
        calc_mask_label.setStyleSheet("color: #a7a7a7; font-size: 13px;")
        topo_layout.addWidget(calc_mask_label)
        self.topo_calc_mask_list = MaskListWidget()
        topo_layout.addWidget(self.topo_calc_mask_list)
        apply_mask_label = QLabel("应用掩膜条件 (apply_mask - 取交集):")
        apply_mask_label.setStyleSheet("color: #a7a7a7; font-size: 13px;")
        topo_layout.addWidget(apply_mask_label)
        self.topo_apply_mask_list = MaskListWidget()
        topo_layout.addWidget(self.topo_apply_mask_list)
        self.topo_group.setLayout(topo_layout)
        layout.addWidget(self.topo_group)

        # 3. BRDF 参数 - NVIDIA 绿色主题 (#76b900)
        self.brdf_group = QGroupBox("BRDF 校正 (BRDF Correction)")
        self.brdf_group.setVisible(False)  # 默认隐藏
        self.brdf_group.setStyleSheet("""
            QGroupBox {
                background-color: #1a1a1a;
                border: 2px solid #76b900;
                border-radius: 2px;
                color: #ffffff;
                font-weight: 700;
                font-size: 14px;
                padding: 10px;
                margin-top: 15px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 5px 10px;
                color: #000000;
                background-color: #76b900;
                border-radius: 2px;
                font-weight: 700;
                font-size: 14px;
            }
            QLabel {
                color: #ffffff;
                font-size: 14px;
                font-weight: 700;
            }
            QComboBox {
                background-color: #000000;
                color: #ffffff;
                border: 1px solid #76b900;
                border-radius: 2px;
                padding: 5px;
                min-width: 150px;
            }
            QComboBox::drop-down {
                border: none;
                width: 25px;
            }
            QComboBox::down-arrow {
                image: none;
                border-left: 5px solid transparent;
                border-right: 5px solid transparent;
                border-top: 6px solid #76b900;
                margin-right: 8px;
            }
            QComboBox QAbstractItemView {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #76b900;
                selection-background-color: #76b900;
                selection-color: #000000;
            }
            QCheckBox {
                color: #ffffff;
                font-size: 14px;
                font-weight: 700;
                spacing: 8px;
            }
            QCheckBox::indicator {
                width: 18px;
                height: 18px;
                border: 2px solid #76b900;
                border-radius: 2px;
                background-color: transparent;
            }
            QCheckBox::indicator:checked {
                background-color: #76b900;
            }
            QDoubleSpinBox, QSpinBox {
                background-color: #000000;
                color: #ffffff;
                border: 1px solid #76b900;
                border-radius: 2px;
                padding: 5px;
            }
        """)
        brdf_layout = QFormLayout()
        brdf_layout.setVerticalSpacing(10)
        brdf_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
        brdf_layout.setLabelAlignment(Qt.AlignLeft)
        brdf_layout.setFormAlignment(Qt.AlignLeft)

        self.brdf_type = NoWheelComboBox()
        self.brdf_type.addItems(["flex", "universal"])
        self.brdf_type.setMinimumWidth(300)
        brdf_layout.addRow("算法类型 (type):", self.brdf_type)
        self.brdf_grouped = QCheckBox("启用分组 (grouped)")
        self.brdf_grouped.setChecked(True)
        brdf_layout.addRow("", self.brdf_grouped)
        self.geometric_kernel = NoWheelComboBox()
        self.geometric_kernel.addItems(["li_sparse", "li_dense", "li_sparse_r", "li_dense_r", "roujean"])
        self.geometric_kernel.setMinimumWidth(300)
        brdf_layout.addRow("几何核 (geometric):", self.geometric_kernel)
        self.volume_kernel = NoWheelComboBox()
        self.volume_kernel.addItems(["ross_thin", "ross_thick", "hotspot", "roujean"])
        self.volume_kernel.setMinimumWidth(300)
        brdf_layout.addRow("体积核 (volume):", self.volume_kernel)
        self.br_b_ratio = NoWheelDoubleSpinBox()
        self.br_b_ratio.setRange(0.1, 10.0)
        self.br_b_ratio.setValue(2.5)
        self.br_b_ratio.setDecimals(2)
        self.br_b_ratio.setMinimumWidth(300)
        brdf_layout.addRow("b/r 比率:", self.br_b_ratio)
        self.hb_ratio = NoWheelDoubleSpinBox()
        self.hb_ratio.setRange(0.1, 5.0)
        self.hb_ratio.setValue(2.0)
        self.hb_ratio.setDecimals(2)
        self.hb_ratio.setMinimumWidth(300)
        brdf_layout.addRow("h/b 比率:", self.hb_ratio)
        self.sample_perc = NoWheelDoubleSpinBox()
        self.sample_perc.setRange(0.01, 1.0)
        self.sample_perc.setValue(0.1)
        self.sample_perc.setDecimals(2)
        self.sample_perc.setMinimumWidth(300)
        brdf_layout.addRow("采样比例 (sample_perc):", self.sample_perc)
        self.interp_kind = NoWheelComboBox()
        self.interp_kind.addItems(["linear", "nearest", "cubic"])
        self.interp_kind.setMinimumWidth(300)
        brdf_layout.addRow("插值方法 (interp_kind):", self.interp_kind)
        # BRDF 掩膜
        calc_mask_label_brdf = QLabel("计算掩膜条件 (calc_mask):")
        calc_mask_label_brdf.setStyleSheet("color: #a7a7a7; font-size: 13px;")
        brdf_layout.addRow(calc_mask_label_brdf)
        self.brdf_calc_mask_list = MaskListWidget()
        brdf_layout.addRow(self.brdf_calc_mask_list)
        apply_mask_label_brdf = QLabel("应用掩膜条件 (apply_mask):")
        apply_mask_label_brdf.setStyleSheet("color: #a7a7a7; font-size: 13px;")
        brdf_layout.addRow(apply_mask_label_brdf)
        self.brdf_apply_mask_list = MaskListWidget()
        brdf_layout.addRow(self.brdf_apply_mask_list)
        # bin_type 等
        self.bin_type = NoWheelComboBox()
        self.bin_type.addItems(["dynamic", "fixed"])
        self.bin_type.setMinimumWidth(300)
        brdf_layout.addRow("分箱类型 (bin_type):", self.bin_type)
        self.num_bins = NoWheelSpinBox()
        self.num_bins.setRange(1, 100)
        self.num_bins.setValue(18)
        self.num_bins.setMinimumWidth(300)
        brdf_layout.addRow("分箱数量 (num_bins):", self.num_bins)
        self.ndvi_bin_min = NoWheelDoubleSpinBox()
        self.ndvi_bin_min.setRange(0.0, 1.0)
        self.ndvi_bin_min.setValue(0.05)
        self.ndvi_bin_min.setDecimals(2)
        self.ndvi_bin_min.setMinimumWidth(300)
        brdf_layout.addRow("NDVI 最小值 (ndvi_bin_min):", self.ndvi_bin_min)
        self.ndvi_bin_max = NoWheelDoubleSpinBox()
        self.ndvi_bin_max.setRange(0.0, 1.0)
        self.ndvi_bin_max.setValue(1.0)
        self.ndvi_bin_max.setDecimals(2)
        self.ndvi_bin_max.setMinimumWidth(300)
        brdf_layout.addRow("NDVI 最大值 (ndvi_bin_max):", self.ndvi_bin_max)
        self.ndvi_perc_min = NoWheelSpinBox()
        self.ndvi_perc_min.setRange(0, 100)
        self.ndvi_perc_min.setValue(10)
        self.ndvi_perc_min.setMinimumWidth(300)
        brdf_layout.addRow("NDVI 百分比最小值 (ndvi_perc_min):", self.ndvi_perc_min)
        self.ndvi_perc_max = NoWheelSpinBox()
        self.ndvi_perc_max.setRange(0, 100)
        self.ndvi_perc_max.setValue(95)
        self.ndvi_perc_max.setMinimumWidth(300)
        brdf_layout.addRow("NDVI 百分比最大值 (ndvi_perc_max):", self.ndvi_perc_max)
        self.solar_zn_type = NoWheelComboBox()
        self.solar_zn_type.addItems(["scene", "pixel"])
        self.solar_zn_type.setMinimumWidth(300)
        brdf_layout.addRow("太阳天顶角类型 (solar_zn_type):", self.solar_zn_type)
        self.brdf_group.setLayout(brdf_layout)
        layout.addWidget(self.brdf_group)

        # 4. Glint 参数 - 橙色主题 (#df6500)
        self.glint_group = QGroupBox("耀斑校正 (Glint Correction)")
        self.glint_group.setVisible(False)  # 默认隐藏
        self.glint_group.setStyleSheet("""
            QGroupBox {
                background-color: #1a1a1a;
                border: 2px solid #df6500;
                border-radius: 2px;
                color: #ffffff;
                font-weight: 700;
                font-size: 14px;
                padding: 10px;
                margin-top: 15px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 5px 10px;
                color: #ffffff;
                background-color: #df6500;
                border-radius: 2px;
                font-weight: 700;
                font-size: 14px;
            }
            QLabel {
                color: #ffffff;
                font-size: 14px;
                font-weight: 700;
            }
            QComboBox {
                background-color: #000000;
                color: #ffffff;
                border: 1px solid #df6500;
                border-radius: 2px;
                padding: 5px;
                min-width: 150px;
            }
            QComboBox::drop-down {
                border: none;
                width: 25px;
            }
            QComboBox::down-arrow {
                image: none;
                border-left: 5px solid transparent;
                border-right: 5px solid transparent;
                border-top: 6px solid #df6500;
                margin-right: 8px;
            }
            QComboBox QAbstractItemView {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #df6500;
                selection-background-color: #df6500;
            }
            QSpinBox {
                background-color: #000000;
                color: #ffffff;
                border: 1px solid #df6500;
                border-radius: 2px;
                padding: 5px;
            }
        """)
        glint_layout = QFormLayout()
        glint_layout.setVerticalSpacing(10)
        glint_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
        glint_layout.setLabelAlignment(Qt.AlignLeft)
        glint_layout.setFormAlignment(Qt.AlignLeft)

        self.glint_type = NoWheelComboBox()
        self.glint_type.addItems(["hochberg", "gao", "hedley"])
        self.glint_type.setMinimumWidth(300)
        glint_layout.addRow("校正算法 (type):", self.glint_type)
        self.correction_wave = NoWheelSpinBox()
        self.correction_wave.setRange(1000, 2500)
        self.correction_wave.setValue(2150)
        self.correction_wave.setMinimumWidth(300)
        glint_layout.addRow("校正波长 (nm):", self.correction_wave)
        apply_mask_label_glint = QLabel("应用掩膜条件 (apply_mask):")
        apply_mask_label_glint.setStyleSheet("color: #a7a7a7; font-size: 13px;")
        glint_layout.addRow(apply_mask_label_glint)
        self.glint_apply_mask_list = MaskListWidget()
        glint_layout.addRow(self.glint_apply_mask_list)
        self.glint_group.setLayout(glint_layout)
        layout.addWidget(self.glint_group)

        layout.addStretch()
        self.corr_tab.setLayout(QVBoxLayout())
        self.corr_tab.layout().addWidget(scroll)
        
        # 设置容器背景色
        container.setStyleSheet("background-color: #000000;")
        
        # 初始化校正参数组显示状态
        self.on_correction_changed()

    def on_correction_changed(self):
        """根据选择的校正类型显示/隐藏对应的参数组"""
        # 显示/隐藏对应的参数组
        if hasattr(self, 'topo_group'):
            self.topo_group.setVisible(self.corr_topo_cb.isChecked())
        if hasattr(self, 'brdf_group'):
            self.brdf_group.setVisible(self.corr_brdf_cb.isChecked())
        if hasattr(self, 'glint_group'):
            self.glint_group.setVisible(self.corr_glint_cb.isChecked())

    # ==================== 输出设置页 ====================
    def setup_output_tab(self):
        layout = QFormLayout(self.output_tab)
        layout.setVerticalSpacing(12)
        layout.setHorizontalSpacing(10)
        layout.setContentsMargins(15, 15, 15, 15)
        layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
        layout.setLabelAlignment(Qt.AlignLeft)
        layout.setFormAlignment(Qt.AlignLeft)

        # 设置输出设置页的样式
        self.output_tab.setStyleSheet("""
            QLabel {
                color: #ffffff;
                font-size: 14px;
                font-weight: 700;
            }
            QLineEdit {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #76b900;
                border-radius: 2px;
                padding: 5px;
                min-width: 300px;
            }
            QLineEdit:focus {
                border: 2px solid #76b900;
            }
            QSpinBox {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #76b900;
                border-radius: 2px;
                padding: 5px;
                min-width: 300px;
            }
            QSpinBox:focus {
                border: 2px solid #76b900;
            }
            QCheckBox {
                color: #ffffff;
                font-size: 14px;
                font-weight: 700;
                spacing: 8px;
            }
            QCheckBox::indicator {
                width: 18px;
                height: 18px;
                border: 2px solid #76b900;
                border-radius: 2px;
                background-color: transparent;
            }
            QCheckBox::indicator:checked {
                background-color: #76b900;
            }
        """)

        # 输出目录
        self.output_dir_edit = QLineEdit()
        self.output_dir_edit.setPlaceholderText("选择输出目录...")
        btn_out = QPushButton("浏览...")
        btn_out.clicked.connect(lambda: self.select_folder(self.output_dir_edit))
        layout.addRow("输出目录 (Output Directory):", self._horizontal_widget(self.output_dir_edit, btn_out))

        # 后缀
        self.suffix_edit = QLineEdit("topo_brdf_glint")
        self.suffix_edit.setPlaceholderText("输出文件名后缀")
        layout.addRow("后缀 (Suffix):", self.suffix_edit)

        # 导出选项
        export_label = QLabel("导出选项 (Export Options):")
        export_label.setStyleSheet("color: #76b900; font-size: 14px; font-weight: 700;")
        layout.addRow(export_label)

        self.coeffs_cb = QCheckBox("输出系数 (coeffs)")
        self.image_cb = QCheckBox("输出图像 (image)")
        self.masks_cb = QCheckBox("输出掩膜 (masks)")
        self.coeffs_cb.setChecked(False)
        self.image_cb.setChecked(True)
        self.masks_cb.setChecked(True)
        layout.addRow("", self.coeffs_cb)
        layout.addRow("", self.image_cb)
        layout.addRow("", self.masks_cb)

        # 子集波段
        self.subset_waves_edit = QLineEdit("")
        self.subset_waves_edit.setPlaceholderText("例如: 450, 550, 650 (逗号分隔，留空导出全部)")
        layout.addRow("子集波段 (Subset Waves):", self.subset_waves_edit)

        # CPU核心数
        self.num_cpus_spin = NoWheelSpinBox()
        self.num_cpus_spin.setRange(1, 64)
        self.num_cpus_spin.setValue(2)
        self.num_cpus_spin.setMinimumWidth(300)
        layout.addRow("CPU核心数 (num_cpus):", self.num_cpus_spin)

        # 重采样
        self.resample_cb = QCheckBox("启用重采样 (Resample)")
        layout.addRow("", self.resample_cb)

    # ==================== 配置加载与保存 ====================
    def load_config(self):
        file_path, _ = QFileDialog.getOpenFileName(self, "加载 JSON 配置", "", "JSON Files (*.json)")
        if not file_path:
            return
        try:
            with open(file_path, 'r') as f:
                config = json.load(f)
        except Exception as e:
            QMessageBox.critical(self, "错误", f"无法加载 JSON: {e}")
            return

        # 文件与波段映射 - 从 anc_files 恢复文件对和波段映射
        if "anc_files" in config:
            self.band_mappings = {}
            self.file_pairs = []
            self.file_list_widget.clear()
            
            for rfl_path, anc_info in config["anc_files"].items():
                # 恢复波段映射
                mapping = {}
                obs_path = None
                for param, val in anc_info.items():
                    if isinstance(val, list) and len(val) >= 2:
                        mapping[param] = val[1]
                        # 提取第一个参数的obs路径作为该文件对的obs路径
                        if obs_path is None:
                            obs_path = val[0]
                self.band_mappings[rfl_path] = mapping
                
                # 恢复文件对（不检查文件是否存在，支持加载其他机器创建的配置）
                if obs_path:
                    self.file_pairs.append((rfl_path, obs_path))
                    rfl_basename = os.path.basename(rfl_path)
                    obs_basename = os.path.basename(obs_path)
                    # 标记文件是否存在
                    exists_marker = "" if os.path.exists(rfl_path) else " [文件不存在]"
                    self.file_list_widget.addItem(f"{rfl_basename}  ->  {obs_basename}{exists_marker}")
            
            # 自动选择第一个文件并刷新显示
            if self.file_pairs:
                self.file_list_widget.setCurrentRow(0)
                # 先设置文件夹路径（在on_file_selected之前）
                first_rfl_path = self.file_pairs[0][0]
                first_obs_path = self.file_pairs[0][1]
                input_dir = os.path.dirname(first_rfl_path)
                anc_dir = os.path.dirname(first_obs_path)
                self.input_folder_edit.setText(input_dir)
                self.anc_folder_edit.setText(anc_dir)
                # 触发UI更新
                QApplication.processEvents()
                # 然后选择文件并刷新波段映射
                self.on_file_selected(self.file_list_widget.item(0))

        # corrections
        corrections = config.get("corrections", [])
        self.corr_topo_cb.setChecked("topo" in corrections)
        self.corr_brdf_cb.setChecked("brdf" in corrections)
        self.corr_glint_cb.setChecked("glint" in corrections)
        
        # 更新参数组显示状态
        self.on_correction_changed()

        # topo
        topo = config.get("topo", {})
        self.topo_type.setCurrentText(topo.get("type", "scs+c"))
        self.c_fit_type.setCurrentText(topo.get("c_fit_type", "nnls"))
        self.topo_calc_mask_list.set_conditions(topo.get("calc_mask", []))
        self.topo_apply_mask_list.set_conditions(topo.get("apply_mask", []))

        # brdf
        brdf = config.get("brdf", {})
        self.brdf_type.setCurrentText(brdf.get("type", "flex"))
        self.brdf_grouped.setChecked(brdf.get("grouped", True))
        self.geometric_kernel.setCurrentText(brdf.get("geometric", "li_dense_r"))
        self.volume_kernel.setCurrentText(brdf.get("volume", "ross_thick"))
        self.br_b_ratio.setValue(brdf.get("b/r", 2.5))
        self.hb_ratio.setValue(brdf.get("h/b", 2.0))
        self.sample_perc.setValue(brdf.get("sample_perc", 0.1))
        self.interp_kind.setCurrentText(brdf.get("interp_kind", "linear"))
        self.brdf_calc_mask_list.set_conditions(brdf.get("calc_mask", []))
        self.brdf_apply_mask_list.set_conditions(brdf.get("apply_mask", []))
        self.bin_type.setCurrentText(brdf.get("bin_type", "dynamic"))
        self.num_bins.setValue(brdf.get("num_bins", 18))
        self.ndvi_bin_min.setValue(brdf.get("ndvi_bin_min", 0.05))
        self.ndvi_bin_max.setValue(brdf.get("ndvi_bin_max", 1.0))
        self.ndvi_perc_min.setValue(brdf.get("ndvi_perc_min", 10))
        self.ndvi_perc_max.setValue(brdf.get("ndvi_perc_max", 95))
        self.solar_zn_type.setCurrentText(brdf.get("solar_zn_type", "scene"))

        # glint
        glint = config.get("glint", {})
        self.glint_type.setCurrentText(glint.get("type", "hochberg"))
        self.correction_wave.setValue(glint.get("correction_wave", 2150))
        self.glint_apply_mask_list.set_conditions(glint.get("apply_mask", []))

        # export
        export = config.get("export", {})
        self.output_dir_edit.setText(export.get("output_dir", ""))
        self.suffix_edit.setText(export.get("suffix", "topo_brdf_glint"))
        self.coeffs_cb.setChecked(export.get("coeffs", False))
        self.image_cb.setChecked(export.get("image", True))
        self.masks_cb.setChecked(export.get("masks", True))
        subset = export.get("subset_waves", [])
        self.subset_waves_edit.setText(",".join(str(w) for w in subset))
        self.num_cpus_spin.setValue(config.get("num_cpus", 2))
        self.resample_cb.setChecked(config.get("resample", False))

        QMessageBox.information(self, "成功", f"已加载配置: {file_path}")

    def save_config(self):
        if not self.file_pairs:
            QMessageBox.warning(self, "警告", "没有有效的文件对，请先匹配文件。")
            return

        config = {}
        config["input_files"] = [rfl for rfl, _ in self.file_pairs]
        config["file_type"] = "envi"
        config["bad_bands"] = []
        config["num_cpus"] = self.num_cpus_spin.value()

        anc_files_dict = {}
        for rfl_path, obs_path in self.file_pairs:
            mapping = self.band_mappings.get(rfl_path, {})
            anc_entry = {}
            for param, spin in self.param_spinboxes.items():
                band_idx = mapping.get(param, spin.value())
                anc_entry[param] = [obs_path, band_idx]
            anc_files_dict[rfl_path] = anc_entry
        config["anc_files"] = anc_files_dict

        corrections = []
        if self.corr_topo_cb.isChecked():
            corrections.append("topo")
        if self.corr_brdf_cb.isChecked():
            corrections.append("brdf")
        if self.corr_glint_cb.isChecked():
            corrections.append("glint")
        config["corrections"] = corrections

        config["topo"] = {
            "type": self.topo_type.currentText(),
            "calc_mask": self.topo_calc_mask_list.get_conditions(),
            "apply_mask": self.topo_apply_mask_list.get_conditions(),
            "c_fit_type": self.c_fit_type.currentText()
        }

        config["brdf"] = {
            "type": self.brdf_type.currentText(),
            "grouped": self.brdf_grouped.isChecked(),
            "geometric": self.geometric_kernel.currentText(),
            "volume": self.volume_kernel.currentText(),
            "b/r": self.br_b_ratio.value(),
            "h/b": self.hb_ratio.value(),
            "sample_perc": self.sample_perc.value(),
            "interp_kind": self.interp_kind.currentText(),
            "calc_mask": self.brdf_calc_mask_list.get_conditions(),
            "apply_mask": self.brdf_apply_mask_list.get_conditions(),
            "bin_type": self.bin_type.currentText(),
            "num_bins": self.num_bins.value(),
            "ndvi_bin_min": self.ndvi_bin_min.value(),
            "ndvi_bin_max": self.ndvi_bin_max.value(),
            "ndvi_perc_min": self.ndvi_perc_min.value(),
            "ndvi_perc_max": self.ndvi_perc_max.value(),
            "solar_zn_type": self.solar_zn_type.currentText()
        }

        config["glint"] = {
            "type": self.glint_type.currentText(),
            "correction_wave": self.correction_wave.value(),
            "apply_mask": self.glint_apply_mask_list.get_conditions()
        }

        subset_waves = []
        if self.subset_waves_edit.text().strip():
            try:
                subset_waves = [int(x.strip()) for x in self.subset_waves_edit.text().split(",") if x.strip()]
            except:
                pass
        config["export"] = {
            "coeffs": self.coeffs_cb.isChecked(),
            "image": self.image_cb.isChecked(),
            "masks": self.masks_cb.isChecked(),
            "subset_waves": subset_waves,
            "output_dir": self.output_dir_edit.text(),
            "suffix": self.suffix_edit.text()
        }
        config["resample"] = self.resample_cb.isChecked()

        save_path, _ = QFileDialog.getSaveFileName(self, "保存 JSON 配置", "", "JSON Files (*.json)")
        if not save_path:
            return
        try:
            with open(save_path, 'w') as f:
                json.dump(config, f, indent=3)
            QMessageBox.information(self, "成功", f"配置已保存至: {save_path}")
        except Exception as e:
            QMessageBox.critical(self, "错误", f"保存失败: {e}")


# ==================== 水体BRDF模块窗口 ====================
class WaterBRDFWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("水体 BRDF 校正 - Ocean BRDF")
        self.setMinimumSize(1000, 700)
        
        # 命令执行相关
        self.command_thread = None
        
        # 设置样式和界面
        self.setup_water_styles()
        self.setup_water_interface()
        
    def setup_water_styles(self):
        """设置水体BRDF模块的样式"""
        self.setStyleSheet("""
            QMainWindow {
                background-color: #000000;
            }
            QWidget {
                background-color: #000000;
                color: #ffffff;
                font-family: Arial, Helvetica, sans-serif;
                font-size: 14px;
            }
            QGroupBox {
                background-color: #1a1a1a;
                border: 2px solid #0080ff;
                border-radius: 8px;
                margin-top: 15px;
                padding-top: 15px;
                font-weight: bold;
                font-size: 16px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 15px;
                padding: 5px 10px;
                color: #ffffff;
                background-color: #0080ff;
                border-radius: 4px;
                font-weight: bold;
            }
            QPushButton {
                background-color: transparent;
                color: #ffffff;
                border: 2px solid #0080ff;
                border-radius: 6px;
                padding: 10px 20px;
                font-weight: bold;
                font-size: 14px;
            }
            QPushButton:hover {
                background-color: #0080ff;
                color: #000000;
            }
            QPushButton:pressed {
                background-color: #0066cc;
                border-color: #0066cc;
            }
            QLineEdit {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #0080ff;
                border-radius: 4px;
                padding: 8px;
                font-size: 14px;
            }
            QLineEdit:focus {
                border: 2px solid #0080ff;
            }
            QComboBox {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #0080ff;
                border-radius: 4px;
                padding: 8px;
                font-size: 14px;
            }
            QComboBox::drop-down {
                border: none;
                width: 25px;
            }
            QComboBox::down-arrow {
                image: none;
                border-left: 5px solid transparent;
                border-right: 5px solid transparent;
                border-top: 6px solid #0080ff;
                margin-right: 8px;
            }
            QComboBox QAbstractItemView {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #0080ff;
                selection-background-color: #0080ff;
            }
            QCheckBox {
                color: #ffffff;
                font-size: 14px;
                spacing: 8px;
            }
            QCheckBox::indicator {
                width: 18px;
                height: 18px;
                border: 2px solid #0080ff;
                border-radius: 2px;
                background-color: transparent;
            }
            QCheckBox::indicator:checked {
                background-color: #0080ff;
            }
            QSpinBox, QDoubleSpinBox {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #0080ff;
                border-radius: 4px;
                padding: 5px;
            }
            QTextEdit {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #0080ff;
                border-radius: 4px;
                font-family: 'Consolas', 'Monaco', monospace;
                font-size: 12px;
            }
            QProgressBar {
                background-color: #1a1a1a;
                border: 1px solid #0080ff;
                border-radius: 4px;
                text-align: center;
                color: #ffffff;
            }
            QProgressBar::chunk {
                background-color: #0080ff;
                border-radius: 4px;
            }
        """)
        
    def setup_water_interface(self):
        """创建水体BRDF模块界面"""
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        main_layout.setContentsMargins(30, 30, 30, 30)
        main_layout.setSpacing(20)
        
        # 标题
        title_label = QLabel("水体 BRDF 校正")
        title_label.setAlignment(Qt.AlignCenter)
        title_label.setStyleSheet("""
            font-size: 28px;
            font-weight: bold;
            color: #0080ff;
            margin-bottom: 10px;
        """)
        main_layout.addWidget(title_label)
        
        subtitle_label = QLabel("Ocean BRDF Correction - 支持 L11, M02, M02SeaDAS, O25 模型")
        subtitle_label.setAlignment(Qt.AlignCenter)
        subtitle_label.setStyleSheet("""
            font-size: 14px;
            color: #cccccc;
            margin-bottom: 20px;
        """)
        main_layout.addWidget(subtitle_label)
        
        # 创建分割器：左侧配置，右侧日志
        splitter = QSplitter(Qt.Horizontal)
        
        # 左侧：参数配置
        left_widget = QWidget()
        left_layout = QVBoxLayout(left_widget)
        
        # 输入文件组
        input_group = QGroupBox("输入文件")
        input_layout = QFormLayout(input_group)
        input_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
        input_layout.setLabelAlignment(Qt.AlignLeft)
        input_layout.setFormAlignment(Qt.AlignLeft)
        
        self.hyperspectral_edit = QLineEdit()
        self.hyperspectral_edit.setPlaceholderText("选择高光谱数据文件 (.img, .dat, .hdr)")
        hyperspectral_btn = QPushButton("浏览...")
        hyperspectral_btn.clicked.connect(lambda: self.browse_file(self.hyperspectral_edit, "高光谱数据文件", "ENVI Files (*.img *.dat *.hdr);;All Files (*)"))
        input_layout.addRow("高光谱文件:", self.create_file_row(self.hyperspectral_edit, hyperspectral_btn))
        
        self.angle_edit = QLineEdit()
        self.angle_edit.setPlaceholderText("选择角度文件 (.dat, .bsq, .bip, .bil, .hdr)")
        angle_btn = QPushButton("浏览...")
        angle_btn.clicked.connect(lambda: self.browse_file(self.angle_edit, "角度文件", "ENVI Files (*.dat *.bsq *.bip *.bil *.hdr);;All Files (*)"))
        input_layout.addRow("角度文件:", self.create_file_row(self.angle_edit, angle_btn))
        
        self.mask_edit = QLineEdit()
        self.mask_edit.setPlaceholderText("选择水体掩膜文件 (.tif, .tiff)")
        mask_btn = QPushButton("浏览...")
        mask_btn.clicked.connect(lambda: self.browse_file(self.mask_edit, "水体掩膜文件", "GeoTIFF Files (*.tif *.tiff);;All Files (*)"))
        input_layout.addRow("水体掩膜:", self.create_file_row(self.mask_edit, mask_btn))
        
        left_layout.addWidget(input_group)
        
        # BRDF模型参数组
        model_group = QGroupBox("BRDF 模型参数")
        model_layout = QFormLayout(model_group)
        model_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
        model_layout.setLabelAlignment(Qt.AlignLeft)
        model_layout.setFormAlignment(Qt.AlignLeft)
        
        self.brdf_model_combo = NoWheelComboBox()
        self.brdf_model_combo.addItems(["L11", "M02",  "O25"])
        self.brdf_model_combo.setCurrentText("L11")
        model_layout.addRow("BRDF 模型:", self.brdf_model_combo)
        
        # 输出变量多选列表
        output_vars_label = QLabel("输出变量:")
        output_vars_label.setStyleSheet("color: #ffffff; font-weight: bold;")
        model_layout.addRow(output_vars_label)
        
        self.output_vars_list = QListWidget()
        self.output_vars_list.setSelectionMode(QListWidget.MultiSelection)
        self.output_vars_list.addItems([
            "Rw_brdf - BRDF校正后的水体反射率",
            "rho_ex_w - 水体反射率 (nrrs * π)",
            "nrrs - 归一化遥感反射率",
            "C_brdf - BRDF校正因子",
            "brdf_unc - BRDF不确定度",
            "nrrs_unc - nrrs不确定度"
        ])
        # 默认选中 Rw_brdf, nrrs, C_brdf
        self.output_vars_list.item(0).setSelected(True)
        self.output_vars_list.item(2).setSelected(True)
        self.output_vars_list.item(3).setSelected(True)
        self.output_vars_list.setMaximumHeight(120)
        self.output_vars_list.setStyleSheet("""
            QListWidget {
                background-color: #1a1a1a;
                color: #ffffff;
                border: 1px solid #0080ff;
                border-radius: 4px;
                padding: 5px;
            }
            QListWidget::item {
                padding: 5px;
                border-radius: 2px;
            }
            QListWidget::item:selected {
                background-color: #0080ff;
                color: #ffffff;
            }
            QListWidget::item:hover {
                background-color: #0046a4;
            }
        """)
        model_layout.addRow(self.output_vars_list)
        
        # 添加提示标签
        hint_label = QLabel("提示：按住 Ctrl 键可多选；brdf_unc 和 nrrs_unc 需要勾选计算不确定性")
        hint_label.setStyleSheet("color: #aaaaaa; font-size: 11px; margin-top: 5px;")
        hint_label.setWordWrap(True)
        model_layout.addRow(hint_label)
        
        self.chunk_size_spin = NoWheelSpinBox()
        self.chunk_size_spin.setRange(1024, 16384)
        self.chunk_size_spin.setValue(4096)
        model_layout.addRow("处理块大小:", self.chunk_size_spin)
        
        self.block_size_spin = NoWheelSpinBox()
        self.block_size_spin.setRange(256, 2048)
        self.block_size_spin.setValue(512)
        model_layout.addRow("空间块大小:", self.block_size_spin)
        
        self.compute_uncertainty_cb = QCheckBox("计算不确定性")
        model_layout.addRow("", self.compute_uncertainty_cb)
        
        left_layout.addWidget(model_group)
        
        # 输出设置组
        output_group = QGroupBox("输出设置")
        output_layout = QFormLayout(output_group)
        output_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
        output_layout.setLabelAlignment(Qt.AlignLeft)
        output_layout.setFormAlignment(Qt.AlignLeft)
        
        self.output_file_edit = QLineEdit()
        self.output_file_edit.setPlaceholderText("输出文件前缀路径")
        output_btn = QPushButton("选择...")
        output_btn.clicked.connect(self.select_output_file)
        output_layout.addRow("输出路径:", self.create_file_row(self.output_file_edit, output_btn))
        
        self.output_format_combo = NoWheelComboBox()
        self.output_format_combo.addItems(["ENVI"])
        output_layout.addRow("输出格式:", self.output_format_combo)
        
        left_layout.addWidget(output_group)
        
        # 运行按钮
        run_btn = QPushButton("运行水体 BRDF 校正")
        run_btn.setStyleSheet("""
            QPushButton {
                background-color: #0080ff;
                color: #ffffff;
                border: 2px solid #0080ff;
                font-weight: bold;
                padding: 12px 24px;
                font-size: 16px;
            }
            QPushButton:hover {
                background-color: #0066cc;
                border-color: #0066cc;
            }
        """)
        run_btn.clicked.connect(self.run_water_brdf)
        left_layout.addWidget(run_btn)
        
        # 右侧：日志和进度显示
        right_widget = QWidget()
        right_layout = QVBoxLayout(right_widget)
        
        log_label = QLabel("执行日志")
        log_label.setStyleSheet("font-weight: bold; color: #0080ff; font-size: 16px;")
        right_layout.addWidget(log_label)
        
        self.log_text = QTextEdit()
        self.log_text.setMaximumWidth(400)
        self.log_text.setPlainText("等待执行命令...\n")
        right_layout.addWidget(self.log_text)
        
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        right_layout.addWidget(self.progress_bar)
        
        # 添加到分割器
        splitter.addWidget(left_widget)
        splitter.addWidget(right_widget)
        splitter.setStretchFactor(0, 2)
        splitter.setStretchFactor(1, 1)
        
        main_layout.addWidget(splitter)
        
    def create_file_row(self, line_edit, button):
        """创建文件选择行"""
        widget = QWidget()
        layout = QHBoxLayout(widget)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(10)
        layout.addWidget(line_edit, 1)
        layout.addWidget(button)
        return widget
        
    def browse_file(self, line_edit, title, file_filter):
        """浏览选择文件"""
        file_path, _ = QFileDialog.getOpenFileName(self, f"选择{title}", "", file_filter)
        if file_path:
            line_edit.setText(file_path)
            
    def select_output_file(self):
        """选择输出文件路径"""
        file_path, _ = QFileDialog.getSaveFileName(self, "选择输出路径", "", "All Files (*)")
        if file_path:
            self.output_file_edit.setText(file_path)
            
    def run_water_brdf(self):
        """运行水体BRDF校正"""
        # 检查必需参数
        hyperspectral_file = self.hyperspectral_edit.text().strip()
        angle_file = self.angle_edit.text().strip()
        mask_file = self.mask_edit.text().strip()
        output_file = self.output_file_edit.text().strip()
        
        if not all([hyperspectral_file, angle_file, mask_file, output_file]):
            QMessageBox.warning(self, "警告", "请填写所有必需的文件路径！")
            return
            
        # 检查文件是否存在
        for file_path, name in [(hyperspectral_file, "高光谱文件"), 
                               (angle_file, "角度文件"), 
                               (mask_file, "掩膜文件")]:
            if not os.path.exists(file_path):
                QMessageBox.warning(self, "警告", f"{name} 不存在：\n{file_path}")
                return
        
        # 构建命令
        ocbrdf_path = os.path.join(os.path.dirname(__file__), "..", "ocbrdf", "ocbrdf_main3.py")
        ocbrdf_path = os.path.abspath(ocbrdf_path)
        ocbrdf_dir = os.path.dirname(ocbrdf_path)
        
        if not os.path.exists(ocbrdf_path):
            QMessageBox.critical(self, "错误", f"找不到 ocbrdf_main3.py 文件:\n{ocbrdf_path}")
            return
        
        # 使用当前Python解释器（而不是简单的'python'命令）
        python_exe = sys.executable

        # 获取选中的输出变量（解析列表项，取空格前的变量名）
        selected_items = self.output_vars_list.selectedItems()
        output_vars = []
        for item in selected_items:
            var_name = item.text().split(' - ')[0].strip()
            output_vars.append(var_name)
        
        if not output_vars:
            QMessageBox.warning(self, "警告", "请至少选择一个输出变量！")
            return

        # 构建命令参数 - 注意：ocbrdf_main3.py 需要从 ocbrdf 目录运行
        # 以确保能正确导入同级目录的模块
        cmd_parts = [
            f'"{python_exe}"',
            f'"{ocbrdf_path}"',
            f'"{hyperspectral_file}"',
            f'"{angle_file}"',
            f'"{mask_file}"',
            f'"{output_file}"',
            f'--brdf-model {self.brdf_model_combo.currentText()}',
            f'--output-var {" ".join(output_vars)}',
            f'--output-format {self.output_format_combo.currentText()}',
            f'--chunk-size {self.chunk_size_spin.value()}',
            f'--block-size {self.block_size_spin.value()}'
        ]
        
        if self.compute_uncertainty_cb.isChecked():
            cmd_parts.append('--compute-uncertainty')
            
        command = ' '.join(cmd_parts)
        
        # 显示进度条和日志
        self.progress_bar.setVisible(True)
        self.progress_bar.setRange(0, 0)  # 不确定进度
        self.log_text.clear()
        self.log_text.append("开始执行水体 BRDF 校正...")
        self.log_text.append(f"BRDF 模型: {self.brdf_model_combo.currentText()}")
        self.log_text.append(f"输出变量: {', '.join(output_vars)}")
        self.log_text.append(f"Python: {python_exe}")
        self.log_text.append(f"命令: {command}\n")
        
        # 启动命令执行线程 - 在ocbrdf目录下执行，确保能找到本地模块
        env = {"PYTHONPATH": ocbrdf_dir}  # 添加ocbrdf目录到Python路径
        self.command_thread = CommandThread(command, ocbrdf_dir, env=env)
        self.command_thread.output_updated.connect(self.append_log)
        self.command_thread.finished_signal.connect(self.command_finished)
        self.command_thread.start()
        
    def append_log(self, text):
        """添加日志文本"""
        self.log_text.append(text)
        cursor = self.log_text.textCursor()
        cursor.movePosition(cursor.End)
        self.log_text.setTextCursor(cursor)
        
    def command_finished(self, success, message):
        """命令执行完成"""
        self.progress_bar.setVisible(False)
        self.log_text.append(f"\n{message}")
        
        if success:
            QMessageBox.information(self, "成功", "水体 BRDF 校正完成！")
        else:
            QMessageBox.warning(self, "警告", f"水体 BRDF 校正失败:\n{message}")


def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()