# -*- coding: utf-8 -*-
import sys
import os
import json
from pathlib import Path

from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QGroupBox,
    QFormLayout, QLabel, QLineEdit, QPushButton, QFileDialog, QComboBox,
    QCheckBox, QSpinBox, QDoubleSpinBox, QTabWidget, QMessageBox, QScrollArea,
    QStackedWidget, QGridLayout, QDialog, QDialogButtonBox,
    QListWidget, QListWidgetItem   # 添加这两个
)
from PyQt5.QtCore import Qt


# ==================== 掩膜条件编辑器 ====================
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 = QComboBox()
        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)

        for param_name, param_type, default_value in params:
            if param_type == int:
                if "band" in param_name or "radius" in param_name:
                    spin = QSpinBox()
                    spin.setRange(0, 3000)
                    spin.setValue(default_value)
                    param_widget = spin
                else:
                    spin = QSpinBox()
                    spin.setRange(-10000, 10000)
                    spin.setValue(default_value)
                    param_widget = spin
            elif param_type == float:
                dspin = QDoubleSpinBox()
                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 = QComboBox()
                    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 = QComboBox()
                    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 MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("BRDF / Topo / Glint 校正配置工具")
        self.setMinimumSize(1100, 800)

        # 存储匹配后的文件对列表
        self.file_pairs = []  # [(rfl_path, obs_path), ...]
        # 存储每个输入文件对应的辅助文件波段映射
        self.band_mappings = {}  # {rfl_path: {param_name: band_index}}

        # 设置全局 NVIDIA 风格样式表
        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;
            }
        """)

        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        main_layout.setSpacing(15)
        main_layout.setContentsMargins(20, 20, 20, 20)

        tabs = QTabWidget()
        tabs.setDocumentMode(True)
        main_layout.addWidget(tabs)

        # 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_layout = QHBoxLayout()
        load_btn = QPushButton("加载 JSON 配置")
        load_btn.clicked.connect(self.load_config)
        save_btn = QPushButton("保存 JSON 配置")
        save_btn.clicked.connect(self.save_config)
        btn_layout.addWidget(load_btn)
        btn_layout.addWidget(save_btn)
        main_layout.addLayout(btn_layout)

    # ==================== 文件与波段映射页 ====================
    def setup_file_tab(self):
        layout = QVBoxLayout(self.file_tab)
        layout.setSpacing(15)
        layout.setContentsMargins(15, 15, 15, 15)

        # 文件夹设置组 - 使用 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)
        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)
        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)
        layout.addWidget(match_btn)

        # 文件列表
        list_label = QLabel("匹配的文件对 (点击编辑波段映射):")
        list_label.setStyleSheet("color: #a7a7a7; font-size: 13px; margin-top: 10px;")
        layout.addWidget(list_label)

        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.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;
            }
        """)
        layout.addWidget(self.file_list_widget)

        # 波段映射组 - 使用蓝色主题
        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 = QFormLayout()
        mapping_layout.setVerticalSpacing(8)
        self.param_spinboxes = {}
        param_names = [
            "path_length", "sensor_az", "sensor_zn", "solar_az", "solar_zn",
            "phase", "slope", "aspect", "cosine_i", "utc_time"
        ]
        for name in param_names:
            spin = QSpinBox()
            spin.setRange(0, 1000)
            spin.setValue(0)
            self.param_spinboxes[name] = spin
            mapping_layout.addRow(f"{name}:", spin)
        mapping_group.setLayout(mapping_layout)
        layout.addWidget(mapping_group)

        self.current_rfl_path = None

    def _horizontal_widget(self, line_edit, button):
        w = QWidget()
        hbox = QHBoxLayout(w)
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.addWidget(line_edit)
        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_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
        if rfl_path in self.band_mappings:
            mapping = self.band_mappings[rfl_path]
            for param, spin in self.param_spinboxes.items():
                spin.setValue(mapping.get(param, 0))
        else:
            for spin in self.param_spinboxes.values():
                spin.setValue(0)

        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_brdf_cb = QCheckBox("BRDF (双向反射分布函数)")
        self.corr_glint_cb = QCheckBox("Glint (耀斑校正)")
        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)
        topo_group = QGroupBox("地形校正 (Topo Correction)")
        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_label = QLabel("类型 (type):")
        type_layout.addWidget(type_label)
        self.topo_type = QComboBox()
        self.topo_type.addItems(["mod_minneart", "scs+c", "cosine", "c", "scs"])
        type_layout.addWidget(self.topo_type)
        type_layout.addStretch()
        topo_layout.addLayout(type_layout)
        # c_fit_type
        fit_layout = QHBoxLayout()
        fit_label = QLabel("拟合类型 (c_fit_type):")
        fit_layout.addWidget(fit_label)
        self.c_fit_type = QComboBox()
        self.c_fit_type.addItems(["nnls", "ols", "wls"])
        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)
        topo_group.setLayout(topo_layout)
        layout.addWidget(topo_group)

        # 3. BRDF 参数 - NVIDIA 绿色主题 (#76b900)
        brdf_group = QGroupBox("BRDF 校正 (BRDF Correction)")
        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)
        self.brdf_type = QComboBox()
        self.brdf_type.addItems(["flex", "universal"])
        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 = QComboBox()
        self.geometric_kernel.addItems(["li_sparse", "li_dense", "li_sparse_r", "li_dense_r", "roujean"])
        brdf_layout.addRow("几何核 (geometric):", self.geometric_kernel)
        self.volume_kernel = QComboBox()
        self.volume_kernel.addItems(["ross_thin", "ross_thick", "hotspot", "roujean"])
        brdf_layout.addRow("体积核 (volume):", self.volume_kernel)
        self.br_b_ratio = QDoubleSpinBox()
        self.br_b_ratio.setRange(0.1, 10.0)
        self.br_b_ratio.setValue(2.5)
        self.br_b_ratio.setDecimals(2)
        brdf_layout.addRow("b/r 比率:", self.br_b_ratio)
        self.hb_ratio = QDoubleSpinBox()
        self.hb_ratio.setRange(0.1, 5.0)
        self.hb_ratio.setValue(2.0)
        self.hb_ratio.setDecimals(2)
        brdf_layout.addRow("h/b 比率:", self.hb_ratio)
        self.sample_perc = QDoubleSpinBox()
        self.sample_perc.setRange(0.01, 1.0)
        self.sample_perc.setValue(0.1)
        self.sample_perc.setDecimals(2)
        brdf_layout.addRow("采样比例 (sample_perc):", self.sample_perc)
        self.interp_kind = QComboBox()
        self.interp_kind.addItems(["linear", "nearest", "cubic"])
        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 = QComboBox()
        self.bin_type.addItems(["dynamic", "fixed"])
        brdf_layout.addRow("分箱类型 (bin_type):", self.bin_type)
        self.num_bins = QSpinBox()
        self.num_bins.setRange(1, 100)
        self.num_bins.setValue(18)
        brdf_layout.addRow("分箱数量 (num_bins):", self.num_bins)
        self.ndvi_bin_min = QDoubleSpinBox()
        self.ndvi_bin_min.setRange(0.0, 1.0)
        self.ndvi_bin_min.setValue(0.05)
        self.ndvi_bin_min.setDecimals(2)
        brdf_layout.addRow("NDVI 最小值 (ndvi_bin_min):", self.ndvi_bin_min)
        self.ndvi_bin_max = QDoubleSpinBox()
        self.ndvi_bin_max.setRange(0.0, 1.0)
        self.ndvi_bin_max.setValue(1.0)
        self.ndvi_bin_max.setDecimals(2)
        brdf_layout.addRow("NDVI 最大值 (ndvi_bin_max):", self.ndvi_bin_max)
        self.ndvi_perc_min = QSpinBox()
        self.ndvi_perc_min.setRange(0, 100)
        self.ndvi_perc_min.setValue(10)
        brdf_layout.addRow("NDVI 百分比最小值 (ndvi_perc_min):", self.ndvi_perc_min)
        self.ndvi_perc_max = QSpinBox()
        self.ndvi_perc_max.setRange(0, 100)
        self.ndvi_perc_max.setValue(95)
        brdf_layout.addRow("NDVI 百分比最大值 (ndvi_perc_max):", self.ndvi_perc_max)
        self.solar_zn_type = QComboBox()
        self.solar_zn_type.addItems(["scene", "pixel"])
        brdf_layout.addRow("太阳天顶角类型 (solar_zn_type):", self.solar_zn_type)
        brdf_group.setLayout(brdf_layout)
        layout.addWidget(brdf_group)

        # 4. Glint 参数 - 橙色主题 (#df6500)
        glint_group = QGroupBox("耀斑校正 (Glint Correction)")
        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)
        self.glint_type = QComboBox()
        self.glint_type.addItems(["hochberg", "gao", "hedley"])
        glint_layout.addRow("校正算法 (type):", self.glint_type)
        self.correction_wave = QSpinBox()
        self.correction_wave.setRange(1000, 2500)
        self.correction_wave.setValue(2150)
        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)
        glint_group.setLayout(glint_layout)
        layout.addWidget(glint_group)

        layout.addStretch()
        self.corr_tab.setLayout(QVBoxLayout())
        self.corr_tab.layout().addWidget(scroll)
        
        # 设置容器背景色
        container.setStyleSheet("background-color: #000000;")

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

        # 设置输出设置页的样式
        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;
            }
            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)

        # 重采样
        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

        # 文件与波段映射（只恢复映射关系，不自动恢复文件对）
        if "anc_files" in config:
            self.band_mappings = {}
            for rfl_path, anc_info in config["anc_files"].items():
                mapping = {}
                for param, val in anc_info.items():
                    if isinstance(val, list) and len(val) >= 2:
                        mapping[param] = val[1]
                self.band_mappings[rfl_path] = mapping
            # 刷新当前选中的文件对映射显示
            if self.file_pairs and self.file_list_widget.currentItem():
                self.on_file_selected(self.file_list_widget.currentItem())

        # 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)

        # 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.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"] = 2

        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}")


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


if __name__ == "__main__":
    main()