"""
可编辑的字典树形显示器
支持字符串编辑时的文件保存/读取和路径选择功能
"""
import sys
import json
import logging
import threading
from pathlib import Path
from typing import Dict, Any, Union
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QTreeWidget, QTreeWidgetItem,
    QVBoxLayout, QWidget, QHBoxLayout, QPushButton, QTextEdit,
    QSplitter, QLabel, QFileDialog, QMessageBox,
    QDialog, QDialogButtonBox, QComboBox, QTextBrowser, QGroupBox,
    QLineEdit, QSpinBox, QDoubleSpinBox, QCheckBox
)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class StringInputDialog(QDialog):
    """字符串输入对话框，支持多种输入方式"""
    
    def __init__(self, parent=None, title="编辑字符串",
                 current_value="", key_name=""):
        super().__init__(parent)
        self.setWindowTitle(title)
        self.setMinimumSize(600, 400)
        self.current_value = current_value
        self.key_name = key_name
        self.result_value = current_value
        
        self.setup_ui()
        
    def setup_ui(self):
        """设置UI界面"""
        layout = QVBoxLayout(self)
        
        # 显示当前编辑的键名
        if self.key_name:
            key_label = QLabel(f"编辑键: {self.key_name}")
            key_label.setFont(QFont("", 10, QFont.Bold))
            layout.addWidget(key_label)
        
        # 输入方式选择
        input_group = QGroupBox("输入方式")
        input_layout = QVBoxLayout(input_group)
        
        # 输入方式选择器
        method_layout = QHBoxLayout()
        method_layout.addWidget(QLabel("选择输入方式:"))
        
        self.method_combo = QComboBox()
        self.method_combo.addItems(["手动输入", "从文件读取", "选择文件路径", "选择文件夹路径"])
        self.method_combo.currentTextChanged.connect(self.on_method_changed)
        method_layout.addWidget(self.method_combo)
        
        input_layout.addLayout(method_layout)
        
        # 操作按钮区域
        button_layout = QHBoxLayout()
        
        self.load_file_btn = QPushButton("选择并读取文件")
        self.load_file_btn.clicked.connect(self.load_from_file)
        self.load_file_btn.setVisible(False)
        button_layout.addWidget(self.load_file_btn)
        
        self.select_file_btn = QPushButton("选择文件路径")
        self.select_file_btn.clicked.connect(self.select_file_path)
        self.select_file_btn.setVisible(False)
        button_layout.addWidget(self.select_file_btn)
        
        self.select_dir_btn = QPushButton("选择文件夹路径")
        self.select_dir_btn.clicked.connect(self.select_dir_path)
        self.select_dir_btn.setVisible(False)
        button_layout.addWidget(self.select_dir_btn)
        
        self.save_file_btn = QPushButton("保存到文件")
        self.save_file_btn.clicked.connect(self.save_to_file)
        button_layout.addWidget(self.save_file_btn)
        
        button_layout.addStretch()
        input_layout.addLayout(button_layout)
        
        layout.addWidget(input_group)
        
        # 文本编辑区域
        edit_group = QGroupBox("内容编辑")
        edit_layout = QVBoxLayout(edit_group)
        
        self.text_edit = QTextEdit()
        self.text_edit.setPlainText(self.current_value)
        self.text_edit.textChanged.connect(self.on_text_changed)
        edit_layout.addWidget(self.text_edit)
        
        layout.addWidget(edit_group)
        
        # 预览区域
        preview_group = QGroupBox("内容预览")
        preview_layout = QVBoxLayout(preview_group)
        
        self.preview_text = QTextBrowser()
        self.preview_text.setMaximumHeight(100)
        self.update_preview()
        preview_layout.addWidget(self.preview_text)
        
        layout.addWidget(preview_group)
        
        # 对话框按钮
        self.button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
        )
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)
        
    def on_method_changed(self, method_name):
        """输入方式改变时的处理"""
        # 隐藏所有按钮
        self.load_file_btn.setVisible(False)
        self.select_file_btn.setVisible(False)
        self.select_dir_btn.setVisible(False)
        
        # 根据选择的方式显示对应按钮
        if method_name == "从文件读取":
            self.load_file_btn.setVisible(True)
        elif method_name == "选择文件路径":
            self.select_file_btn.setVisible(True)
        elif method_name == "选择文件夹路径":
            self.select_dir_btn.setVisible(True)
    
    def on_text_changed(self):
        """文本改变时更新预览和结果"""
        self.result_value = self.text_edit.toPlainText()
        self.update_preview()
    
    def update_preview(self):
        """更新预览内容"""
        content = self.text_edit.toPlainText()
        if len(content) > 200:
            preview = content[:200] + "..."
        else:
            preview = content
        
        self.preview_text.setPlainText(f"内容长度: {len(content)} 字符\n内容预览: {preview}")
    
    def load_from_file(self):
        """从文件读取内容"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, 
            "选择要读取的文件",
            "",
            "文本文件 (*.txt);;JSON文件 (*.json);;所有文件 (*.*)"
        )
        
        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                self.text_edit.setPlainText(content)
                QMessageBox.information(self, "成功", f"已从文件读取内容:\n{file_path}")
            except Exception as e:
                QMessageBox.warning(self, "错误", f"读取文件失败:\n{str(e)}")
    
    def select_file_path(self):
        """选择文件路径"""
        file_path, _ = QFileDialog.getOpenFileName(
            self,
            "选择文件路径",
            "",
            "所有文件 (*.*)"
        )
        
        if file_path:
            # 格式化路径
            formatted_path = self.format_path(file_path)
            self.text_edit.setPlainText(formatted_path)
            
            message = (f"已选择文件路径:\n"
                       f"原始: {file_path}\n"
                       f"格式化: {formatted_path}")
            QMessageBox.information(self, "成功", message)
    
    def select_dir_path(self):
        """选择文件夹路径"""
        dir_path = QFileDialog.getExistingDirectory(
            self,
            "选择文件夹路径"
        )
        
        if dir_path:
            # 格式化路径
            formatted_path = self.format_path(dir_path)
            self.text_edit.setPlainText(formatted_path)
            
            message = (f"已选择文件夹路径:\n"
                       f"原始: {dir_path}\n"
                       f"格式化: {formatted_path}")
            QMessageBox.information(self, "成功", message)
    
    def save_to_file(self):
        """保存内容到文件"""
        content = self.text_edit.toPlainText()
        if not content.strip():
            QMessageBox.warning(self, "警告", "内容为空，无法保存!")
            return
        
        file_path, _ = QFileDialog.getSaveFileName(
            self,
            "保存文件",
            "",
            "文本文件 (*.txt);;JSON文件 (*.json);;所有文件 (*.*)"
        )
        
        if file_path:
            try:
                with open(file_path, 'w', encoding='utf-8') as f:
                    f.write(content)
                QMessageBox.information(self, "成功", f"内容已保存到:\n{file_path}")
            except Exception as e:
                QMessageBox.warning(self, "错误", f"保存文件失败:\n{str(e)}")
    
    def get_value(self):
        """获取编辑结果"""
        return self.result_value
    
    def format_path(self, path_str: str) -> str:
        """
        格式化文件路径，将路径中的单斜杠替换为双斜杠
        
        Args:
            path_str: 原始路径字符串
            
        Returns:
            str: 格式化后的路径字符串
        """
        if not path_str or not isinstance(path_str, str):
            return path_str
        
        # 先将反斜杠替换为正斜杠，统一格式
        normalized_path = path_str.replace('\\', '/')
        
        # 将单斜杠替换为双斜杠
        formatted_path = normalized_path.replace('/', '//')
        
        # 处理盘符格式，确保 C: 变成 C://
        if len(formatted_path) >= 2 and formatted_path[1] == ':':
            # 如果是 C:// 格式，保持不变
            # 如果是 C:其他格式，确保在冒号后添加双斜杠
            if len(formatted_path) == 2:
                formatted_path += '//'
            elif formatted_path[2:4] != '//':
                # 在冒号后插入双斜杠
                formatted_path = (formatted_path[:2] + '//' + 
                                formatted_path[2:].lstrip('/'))
        
        return formatted_path


class ValueEditDialog(QDialog):
    """通用值编辑对话框，支持不同数据类型"""
    
    def __init__(self, parent=None, title="编辑值",
                 current_value=None, key_name="", value_type=None):
        super().__init__(parent)
        self.setWindowTitle(title)
        
        self.current_value = current_value
        self.key_name = key_name
        self.value_type = value_type or type(current_value)
        self.result_value = current_value
        
        # 根据数据类型设置合适的窗口大小
        if self.value_type == str:
            self.setMinimumSize(500, 400)
            self.resize(600, 500)
        else:
            self.setMinimumSize(400, 300)
            self.resize(450, 350)
        
        self.setup_ui()
        
    def setup_ui(self):
        """设置UI界面"""
        layout = QVBoxLayout(self)
        
        # 头部信息区域（固定高度）
        header_widget = QWidget()
        header_layout = QVBoxLayout(header_widget)
        header_layout.setContentsMargins(0, 0, 0, 0)
        
        if self.key_name:
            key_label = QLabel(f"编辑键: {self.key_name}")
            key_label.setFont(QFont("", 10, QFont.Bold))
            header_layout.addWidget(key_label)
        
        type_label = QLabel(f"数据类型: {self.value_type.__name__}")
        type_label.setFont(QFont("", 9))
        header_layout.addWidget(type_label)
        
        header_widget.setMaximumHeight(60)
        layout.addWidget(header_widget)
        
        # 主要编辑区域（可扩展）
        edit_group = QGroupBox("值编辑")
        edit_layout = QVBoxLayout(edit_group)
        
        if self.value_type == str:
            self.edit_widget = QTextEdit()
            self.edit_widget.setPlainText(str(self.current_value))
            self.edit_widget.setMinimumHeight(120)
            self.edit_widget.textChanged.connect(self.on_text_changed)
        elif self.value_type == int:
            self.edit_widget = QSpinBox()
            self.edit_widget.setRange(-2147483648, 2147483647)
            self.edit_widget.setValue(int(self.current_value) if self.current_value is not None else 0)
            self.edit_widget.setMinimumHeight(30)
            self.edit_widget.valueChanged.connect(self.on_int_changed)
        elif self.value_type == float:
            self.edit_widget = QDoubleSpinBox()
            self.edit_widget.setRange(-1e10, 1e10)
            self.edit_widget.setDecimals(15)
            self.edit_widget.setValue(float(self.current_value) if self.current_value is not None else 0.0)
            self.edit_widget.setMinimumHeight(30)
            self.edit_widget.valueChanged.connect(self.on_float_changed)
        elif self.value_type == bool:
            self.edit_widget = QCheckBox("True/False")
            self.edit_widget.setChecked(bool(self.current_value) if self.current_value is not None else False)
            self.edit_widget.setMinimumHeight(30)
            self.edit_widget.stateChanged.connect(self.on_bool_changed)
        else:
            # 对于其他类型，使用文本编辑
            self.edit_widget = QTextEdit()
            self.edit_widget.setPlainText(str(self.current_value))
            self.edit_widget.setMinimumHeight(120)
            self.edit_widget.textChanged.connect(self.on_text_changed)
        
        edit_layout.addWidget(self.edit_widget)
        layout.addWidget(edit_group, 1)  # 设置拉伸因子为1，让编辑区域可扩展
        
        # 类型转换选项（固定高度）
        convert_group = QGroupBox("类型转换")
        convert_layout = QHBoxLayout(convert_group)
        
        convert_layout.addWidget(QLabel("转换为:"))
        self.type_combo = QComboBox()
        self.type_combo.addItems(["str", "int", "float", "bool"])
        self.type_combo.setCurrentText(self.value_type.__name__)
        self.type_combo.currentTextChanged.connect(self.on_type_changed)
        convert_layout.addWidget(self.type_combo)
        
        convert_layout.addStretch()
        convert_group.setMaximumHeight(60)
        layout.addWidget(convert_group)
        
        # 预览区域（固定高度）
        preview_group = QGroupBox("预览")
        preview_layout = QVBoxLayout(preview_group)
        
        self.preview_label = QLabel()
        self.preview_label.setWordWrap(True)
        self.preview_label.setAlignment(Qt.AlignTop)
        self.update_preview()
        preview_layout.addWidget(self.preview_label)
        
        preview_group.setMaximumHeight(100)
        layout.addWidget(preview_group)
        
        # 对话框按钮（固定高度）
        self.button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
        )
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)
    
    def on_text_changed(self):
        """文本改变处理"""
        if isinstance(self.edit_widget, QTextEdit):
            self.result_value = self.edit_widget.toPlainText()
        self.update_preview()
    
    def on_int_changed(self, value):
        """整数改变处理"""
        self.result_value = value
        self.update_preview()
    
    def on_float_changed(self, value):
        """浮点数改变处理"""
        self.result_value = value
        self.update_preview()
    
    def on_bool_changed(self, state):
        """布尔值改变处理"""
        self.result_value = state == 2  # Qt.Checked = 2
        self.update_preview()
    
    def on_type_changed(self, type_name):
        """类型转换处理"""
        try:
            current_text = str(self.result_value)
            
            if type_name == "str":
                self.result_value = current_text
            elif type_name == "int":
                self.result_value = int(float(current_text)) if current_text else 0
            elif type_name == "float":
                self.result_value = float(current_text) if current_text else 0.0
            elif type_name == "bool":
                if current_text.lower() in ('true', '1', 'yes', 'on'):
                    self.result_value = True
                elif current_text.lower() in ('false', '0', 'no', 'off'):
                    self.result_value = False
                else:
                    self.result_value = bool(current_text)
            
            self.update_preview()
        except (ValueError, TypeError) as e:
            QMessageBox.warning(self, "转换错误", f"无法转换为 {type_name} 类型: {str(e)}")
    
    def update_preview(self):
        """更新预览"""
        preview_text = f"当前值: {repr(self.result_value)}\n"
        preview_text += f"类型: {type(self.result_value).__name__}\n"
        preview_text += f"字符串表示: {str(self.result_value)}"
        self.preview_label.setText(preview_text)
    
    def get_value(self):
        """获取编辑结果"""
        return self.result_value


class EditableTreeViewer(QMainWindow):
    """可编辑的字典树形查看器"""
    
    def __init__(self, data_dict: Dict[str, Any], title: str = "可编辑字典查看器"):
        super().__init__()
        self.data_dict = data_dict
        # 用于保护 data_dict 并发读写的锁（外部线程调用 get_data，主线程编辑参数）
        self._data_lock = threading.RLock()
        
        # 保存原始数据的深拷贝，用于重置功能
        try:
            import copy
            self.original_dict = copy.deepcopy(data_dict)
        except Exception as e:
            logger.warning("无法创建原始数据副本: %s", e)
            self.original_dict = data_dict
        
        self.setWindowTitle(title)
        self.setGeometry(100, 100, 1200, 800)
        
        self.setup_ui()
        self.populate_tree()
        
    def setup_ui(self):
        """设置用户界面"""
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 主布局 - 减小边距
        main_layout = QVBoxLayout(central_widget)
        main_layout.setContentsMargins(5, 5, 5, 5)  # 减小主布局边距
        main_layout.setSpacing(3)  # 减小组件间距
        
        # 工具栏 - 紧凑布局
        toolbar_widget = QWidget()
        toolbar_widget.setMaximumHeight(40)  # 限制工具栏高度
        toolbar_layout = QHBoxLayout(toolbar_widget)
        toolbar_layout.setContentsMargins(5, 5, 5, 5)  # 减小边距
        toolbar_layout.setSpacing(5)  # 减小按钮间距
        
        # 创建紧凑的按钮
        self.refresh_btn = QPushButton("刷新")
        self.refresh_btn.setMaximumWidth(60)
        self.refresh_btn.setToolTip("刷新树形图显示")
        self.refresh_btn.clicked.connect(self.refresh_tree)
        toolbar_layout.addWidget(self.refresh_btn)
        
        self.expand_all_btn = QPushButton("展开")
        self.expand_all_btn.setMaximumWidth(60)
        self.expand_all_btn.setToolTip("展开所有节点")
        self.expand_all_btn.clicked.connect(self.expand_all)
        toolbar_layout.addWidget(self.expand_all_btn)
        
        self.collapse_all_btn = QPushButton("折叠")
        self.collapse_all_btn.setMaximumWidth(60)
        self.collapse_all_btn.setToolTip("折叠所有节点")
        self.collapse_all_btn.clicked.connect(self.collapse_all)
        toolbar_layout.addWidget(self.collapse_all_btn)
        
        # 分隔线
        separator = QLabel("|")
        separator.setStyleSheet("color: #CCCCCC;")
        toolbar_layout.addWidget(separator)
        
        self.save_dict_btn = QPushButton("保存")
        self.save_dict_btn.setMaximumWidth(60)
        self.save_dict_btn.setToolTip("保存字典到文件")
        self.save_dict_btn.clicked.connect(self.save_dict_to_file)
        toolbar_layout.addWidget(self.save_dict_btn)
        
        self.load_dict_btn = QPushButton("加载")
        self.load_dict_btn.setMaximumWidth(60)
        self.load_dict_btn.setToolTip("从文件加载字典")
        self.load_dict_btn.clicked.connect(self.load_dict_from_file)
        toolbar_layout.addWidget(self.load_dict_btn)
        
        # 路径格式化按钮
        self.format_path_btn = QPushButton("格式化")
        self.format_path_btn.setMaximumWidth(80)
        self.format_path_btn.setToolTip("格式化选中路径为双斜杠格式")
        self.format_path_btn.clicked.connect(self.format_selected_paths)
        toolbar_layout.addWidget(self.format_path_btn)
        
        # 弹性空间
        toolbar_layout.addStretch()
        
        # 状态标签
        self.status_label = QLabel("就绪")
        self.status_label.setStyleSheet("color: #666666; font-size: 9pt;")
        toolbar_layout.addWidget(self.status_label)
        
        main_layout.addWidget(toolbar_widget)
        
        # 分割器
        splitter = QSplitter(Qt.Horizontal)
        
        # 树形控件
        self.tree_widget = QTreeWidget()
        self.tree_widget.setHeaderLabels(["键", "值", "类型"])
        self.tree_widget.itemDoubleClicked.connect(self.on_item_double_clicked)
        self.tree_widget.setColumnWidth(0, 250)
        self.tree_widget.setColumnWidth(1, 350)
        self.tree_widget.setColumnWidth(2, 80)
        
        splitter.addWidget(self.tree_widget)
        
        # 详情面板
        details_widget = QWidget()
        details_layout = QVBoxLayout(details_widget)
        details_layout.setContentsMargins(5, 5, 5, 5)
        
        # 详情标题
        details_title = QLabel("详细信息")
        details_title.setFont(QFont("", 10, QFont.Bold))
        details_layout.addWidget(details_title)
        
        self.details_text = QTextEdit()
        self.details_text.setReadOnly(True)
        self.details_text.setFont(QFont("Consolas", 9))  # 使用等宽字体
        details_layout.addWidget(self.details_text)
        
        # 操作提示
        tip_label = QLabel("提示：双击值列的项目可以编辑")
        tip_label.setFont(QFont("", 8))
        tip_label.setStyleSheet("color: #666666;")
        details_layout.addWidget(tip_label)
        
        splitter.addWidget(details_widget)
        splitter.setSizes([700, 300])  # 调整初始比例，树形控件占更多空间
        
        main_layout.addWidget(splitter)
        
        # 连接信号
        self.tree_widget.itemSelectionChanged.connect(self.on_selection_changed)
        
        # 添加右键菜单
        self.tree_widget.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tree_widget.customContextMenuRequested.connect(
            self.show_context_menu)
        
    def populate_tree(self):
        """填充树形控件"""
        self.tree_widget.clear()
        if self.data_dict:
            self._add_dict_to_tree(self.data_dict, self.tree_widget.invisibleRootItem(), [])
        self.status_label.setText(f"已加载 {len(self.data_dict)} 个顶级项目")
    
    def _add_dict_to_tree(self, data: Union[Dict, list, Any], parent_item: QTreeWidgetItem, path: list):
        """递归添加字典数据到树形控件"""
        if isinstance(data, dict):
            for key, value in data.items():
                current_path = path + [key]
                item = QTreeWidgetItem(parent_item)
                item.setText(0, str(key))
                item.setData(0, Qt.UserRole, current_path)  # 存储路径信息
                
                if isinstance(value, (dict, list)):
                    item.setText(1, f"({len(value)} 项)" if isinstance(value, dict) else f"[{len(value)} 项]")
                    item.setText(2, "字典" if isinstance(value, dict) else "列表")
                    self._add_dict_to_tree(value, item, current_path)
                else:
                    # 限制显示长度
                    display_value = str(value)
                    if len(display_value) > 100:
                        display_value = display_value[:100] + "..."
                    
                    item.setText(1, display_value)
                    item.setText(2, type(value).__name__)
                    
                    # 所有非容器类型的叶子节点都可以编辑
                    if not isinstance(value, (dict, list)):
                        item.setData(1, Qt.UserRole, "editable")
                        
        elif isinstance(data, list):
            for i, value in enumerate(data):
                current_path = path + [i]
                item = QTreeWidgetItem(parent_item)
                item.setText(0, f"[{i}]")
                item.setData(0, Qt.UserRole, current_path)
                
                if isinstance(value, (dict, list)):
                    item.setText(1, f"({len(value)} 项)" if isinstance(value, dict) else f"[{len(value)} 项]")
                    item.setText(2, "字典" if isinstance(value, dict) else "列表")
                    self._add_dict_to_tree(value, item, current_path)
                else:
                    display_value = str(value)
                    if len(display_value) > 100:
                        display_value = display_value[:100] + "..."
                    
                    item.setText(1, display_value)
                    item.setText(2, type(value).__name__)
                    
                    # 所有非容器类型的叶子节点都可以编辑
                    if not isinstance(value, (dict, list)):
                        item.setData(1, Qt.UserRole, "editable")
    
    def on_item_double_clicked(self, item: QTreeWidgetItem, column: int):
        """处理项目双击事件"""
        if column == 1 and item.data(1, Qt.UserRole) == "editable":
            self.edit_value(item)
    
    def edit_value(self, item: QTreeWidgetItem):
        """编辑值（支持多种数据类型）"""
        path = item.data(0, Qt.UserRole)
        current_value = self.get_value_by_path(path)
        key_name = "/".join(str(p) for p in path)
        
        # 对于字符串类型，使用原有的字符串编辑对话框
        if isinstance(current_value, str):
            self.edit_string_value(item)
            return
        
        # 对于其他类型，使用通用编辑对话框
        dialog = ValueEditDialog(
            self,
            f"编辑{type(current_value).__name__}值",
            current_value,
            key_name,
            type(current_value)
        )
        
        if dialog.exec_() == QDialog.Accepted:
            new_value = dialog.get_value()
            if new_value != current_value:
                # 特殊调试：极耳裁切模型参数
                if "极耳裁切模型参数/模型路径" in key_name:
                    logger.info("🔧 TreeViewer: 准备更新极耳裁切模型路径")
                    logger.info(f"🔧 TreeViewer: 当前值: {current_value}")
                    logger.info(f"🔧 TreeViewer: 新值: {new_value}")
                
                self.set_value_by_path(path, new_value)
                
                # 验证设置是否成功
                if "极耳裁切模型参数/模型路径" in key_name:
                    verify_value = self.get_value_by_path(path)
                    logger.info(f"🔧 TreeViewer: 验证设置后的值: {verify_value}")
                
                # 更新显示
                display_value = str(new_value)
                if len(display_value) > 100:
                    display_value = display_value[:100] + "..."
                item.setText(1, display_value)
                item.setText(2, type(new_value).__name__)
                
                self.status_label.setText(f"已更新: {key_name}")
                logger.info("已更新路径 %s 的值: %s -> %s", key_name, current_value, new_value)
    
    def edit_string_value(self, item: QTreeWidgetItem):
        """编辑字符串值"""
        path = item.data(0, Qt.UserRole)
        current_value = self.get_value_by_path(path)
        key_name = "/".join(str(p) for p in path)
        
        if not isinstance(current_value, str):
            QMessageBox.warning(self, "警告", "只能编辑字符串类型的值!")
            return
        
        dialog = StringInputDialog(
            self, 
            "编辑字符串值", 
            current_value, 
            key_name
        )
        
        if dialog.exec_() == QDialog.Accepted:
            new_value = dialog.get_value()
            if new_value != current_value:
                self.set_value_by_path(path, new_value)
                
                # 更新显示
                display_value = new_value
                if len(display_value) > 100:
                    display_value = display_value[:100] + "..."
                item.setText(1, display_value)
                
                self.status_label.setText(f"已更新: {key_name}")
                logger.info("已更新路径 %s 的值", key_name)
    
    def get_value_by_path(self, path: list):
        """根据路径获取值"""
        with self._data_lock:
            current = self.data_dict
            try:
                for key in path:
                    current = current[key]
                return current
            except (KeyError, IndexError, TypeError):
                return None
    
    def set_value_by_path(self, path: list, value: Any):
        """根据路径设置值"""
        with self._data_lock:
            current = self.data_dict
            try:
                for key in path[:-1]:
                    current = current[key]
                current[path[-1]] = value
            except (KeyError, IndexError, TypeError) as e:
                logger.error("设置值失败: %s", e)
                QMessageBox.warning(self, "错误", f"设置值失败: {str(e)}")
    
    def on_selection_changed(self):
        """处理选择改变事件"""
        selected_items = self.tree_widget.selectedItems()
        if selected_items:
            item = selected_items[0]
            path = item.data(0, Qt.UserRole)
            if path:
                value = self.get_value_by_path(path)
                self.show_details(path, value)
    
    def show_details(self, path: list, value: Any):
        """显示详细信息"""
        path_str = "/".join(str(p) for p in path)
        value_type = type(value).__name__
        
        details = f"路径: {path_str}\n"
        details += f"类型: {value_type}\n"
        
        if isinstance(value, (dict, list)):
            details += f"长度: {len(value)}\n"
            details += f"内容概览:\n"
            
            if isinstance(value, dict):
                for i, (k, v) in enumerate(value.items()):
                    if i >= 10:  # 只显示前10项
                        details += "  ...\n"
                        break
                    details += f"  {k}: {type(v).__name__}\n"
            else:
                for i, v in enumerate(value):
                    if i >= 10:
                        details += "  ...\n"
                        break
                    details += f"  [{i}]: {type(v).__name__}\n"
        else:
            value_str = str(value)
            if len(value_str) > 1000:
                value_str = value_str[:1000] + "..."
            details += f"值:\n{value_str}"
        
        self.details_text.setPlainText(details)
    
    def show_context_menu(self, position):
        """显示右键菜单"""
        from PyQt5.QtWidgets import QMenu, QAction
        
        item = self.tree_widget.itemAt(position)
        if not item:
            return
        
        menu = QMenu(self)
        
        # 格式化路径选项
        format_action = QAction("格式化路径", self)
        format_action.triggered.connect(self.format_selected_paths)
        menu.addAction(format_action)
        
        # 格式化所有路径选项
        format_all_action = QAction("格式化全部路径", self)
        format_all_action.triggered.connect(self.format_all_paths)
        menu.addAction(format_all_action)
        
        menu.addSeparator()
        
        # 刷新选项
        refresh_action = QAction("刷新", self)
        refresh_action.triggered.connect(self.refresh_tree)
        menu.addAction(refresh_action)
        
        # 显示菜单
        menu.exec_(self.tree_widget.mapToGlobal(position))
    
    def refresh_tree(self):
        """刷新树形显示"""
        self.populate_tree()
        self.status_label.setText("树形图已刷新")
    
    def expand_all(self):
        """展开所有项目"""
        self.tree_widget.expandAll()
        self.status_label.setText("已展开全部")
    
    def collapse_all(self):
        """折叠所有项目"""
        self.tree_widget.collapseAll()
        self.status_label.setText("已折叠全部")
    
    def save_dict_to_file(self):
        """保存字典到文件"""
        file_path, _ = QFileDialog.getSaveFileName(
            self,
            "保存字典到文件",
            "",
            "JSON文件 (*.json);;所有文件 (*.*)"
        )
        
        if file_path:
            try:
                import copy
                with self._data_lock:
                    data_snapshot = copy.deepcopy(self.data_dict)
                with open(file_path, 'w', encoding='utf-8') as f:
                    json.dump(data_snapshot, f, ensure_ascii=False, indent=2)
                QMessageBox.information(self, "成功", f"字典已保存到:\n{file_path}")
                self.status_label.setText(f"已保存到: {Path(file_path).name}")
            except Exception as e:
                QMessageBox.warning(self, "错误", f"保存失败:\n{str(e)}")
    
    def load_dict_from_file(self):
        """从文件加载字典"""
        file_path, _ = QFileDialog.getOpenFileName(
            self,
            "从文件加载字典",
            "",
            "JSON文件 (*.json);;所有文件 (*.*)"
        )
        
        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    new_data = json.load(f)
                
                # 更新原始字典
                with self._data_lock:
                    self.data_dict.clear()
                    self.data_dict.update(new_data)
                
                # 刷新显示
                self.populate_tree()
                
                QMessageBox.information(self, "成功", f"已从文件加载字典:\n{file_path}")
                self.status_label.setText(f"已加载: {Path(file_path).name}")
            except Exception as e:
                QMessageBox.warning(self, "错误", f"加载失败:\n{str(e)}")
    
    def format_path(self, path_str: str) -> str:
        """
        格式化文件路径，将Windows风格路径转换为双斜杠格式
        
        Args:
            path_str: 原始路径字符串
            
        Returns:
            str: 格式化后的路径字符串
        """
        if not path_str or not isinstance(path_str, str):
            return path_str
        
        # 将反斜杠替换为双正斜杠
        formatted_path = path_str.replace('\\', '//')
        
        # 如果路径以盘符开头（如 C:），确保格式正确
        if len(formatted_path) >= 2 and formatted_path[1] == ':':
            # 将 C: 转换为 C://
            if len(formatted_path) == 2 or formatted_path[2] != '/':
                drive_part = formatted_path[:2] + '//'
                path_part = formatted_path[2:].lstrip('/')
                formatted_path = drive_part + path_part
        
        return formatted_path
    
    def format_selected_paths(self):
        """格式化选中项目中的所有路径字符串"""
        selected_items = self.tree_widget.selectedItems()
        if not selected_items:
            QMessageBox.information(self, "提示", "请先选择要格式化的项目")
            return
        
        formatted_count = 0
        
        for item in selected_items:
            path = item.data(0, Qt.UserRole)
            if path:
                formatted_count += self._format_path_recursive(path)
        
        if formatted_count > 0:
            # 刷新树形显示
            self.populate_tree()
            self.status_label.setText(f"已格式化 {formatted_count} 个路径")
            message = f"成功格式化了 {formatted_count} 个路径字符串"
            QMessageBox.information(self, "完成", message)
        else:
            QMessageBox.information(self, "提示", "没有找到需要格式化的路径字符串")
    
    def _format_path_recursive(self, path: list) -> int:
        """递归格式化指定路径下的所有路径字符串"""
        current_value = self.get_value_by_path(path)
        formatted_count = 0
        
        if isinstance(current_value, str):
            # 检查是否是路径格式的字符串
            if self._is_path_string(current_value):
                formatted_path = self.format_path(current_value)
                if formatted_path != current_value:
                    self.set_value_by_path(path, formatted_path)
                    formatted_count += 1
                    logger.info("格式化路径: %s -> %s",
                                current_value, formatted_path)
        
        elif isinstance(current_value, dict):
            # 递归处理字典中的所有值
            for key in current_value.keys():
                child_path = path + [key]
                formatted_count += self._format_path_recursive(child_path)
        
        elif isinstance(current_value, list):
            # 递归处理列表中的所有值
            for i in range(len(current_value)):
                child_path = path + [i]
                formatted_count += self._format_path_recursive(child_path)
        
        return formatted_count
    
    def _is_path_string(self, value: str) -> bool:
        """
        判断字符串是否可能是文件路径
        
        Args:
            value: 要检查的字符串
            
        Returns:
            bool: 是否可能是路径字符串
        """
        if not value or len(value) < 3:
            return False
        
        # 检查是否包含路径分隔符
        has_backslash = '\\' in value
        has_forward_slash = '/' in value
        
        # 检查是否包含盘符
        has_drive = (len(value) >= 2 and
                     value[1] == ':' and
                     value[0].isalpha())
        
        # 检查是否包含常见的文件扩展名
        common_extensions = [
            '.txt', '.json', '.xml', '.log', '.cfg', '.ini',
            '.sldasm', '.sldprt', '.dwg', '.pdf', '.doc', '.xls',
            '.py', '.js', '.html', '.css', '.sql', '.bat'
        ]
        has_extension = any(ext.lower() in value.lower()
                            for ext in common_extensions)
        
        # 如果包含反斜杠、盘符或文件扩展名，则认为是路径
        return (has_backslash or
                (has_drive and (has_forward_slash or has_extension)))
    
    def format_all_paths(self):
        """格式化整个数据字典中的所有路径字符串"""
        formatted_count = self._format_path_recursive([])
        
        if formatted_count > 0:
            # 刷新树形显示
            self.populate_tree()
            self.status_label.setText(f"已格式化全部 {formatted_count} 个路径")
            message = f"成功格式化了整个数据中的 {formatted_count} 个路径字符串"
            QMessageBox.information(self, "完成", message)
        else:
            QMessageBox.information(self, "提示", "没有找到需要格式化的路径字符串")
    
    def get_data(self) -> Dict[str, Any]:
        """
        获取经过编辑的数据字典
        返回当前数据字典的深拷贝，确保数据完整性
        线程安全：使用 _data_lock 保护，可被外部线程安全调用
        
        Returns:
            Dict[str, Any]: 编辑后的数据字典
        """
        try:
            # 加锁保护：防止外部线程 deepcopy 期间主线程同时写入 data_dict
            import copy
            with self._data_lock:
                return copy.deepcopy(self.data_dict)
        except Exception as e:
            logger.error("获取数据时发生错误: %s", e)
            # 如果深拷贝失败，尝试返回原始字典
            return self.data_dict
    
    def export_data(self) -> Dict[str, Any]:
        """
        导出数据的别名方法，与get_data功能相同
        
        Returns:
            Dict[str, Any]: 编辑后的数据字典
        """
        return self.get_data()
    
    def get_modified_data(self) -> Dict[str, Any]:
        """
        获取修改后的数据，包含修改统计信息
        
        Returns:
            Dict[str, Any]: 包含数据和元信息的字典
        """
        try:
            import copy
            from datetime import datetime
            
            with self._data_lock:
                data_snapshot = copy.deepcopy(self.data_dict)
            
            # 计算数据统计信息
            def count_items(obj, path=""):
                """递归计算项目数量"""
                if isinstance(obj, dict):
                    count = len(obj)
                    for key, value in obj.items():
                        if isinstance(value, (dict, list)):
                            key_path = f"{path}/{key}" if path else key
                            count += count_items(value, key_path)
                    return count
                elif isinstance(obj, list):
                    count = len(obj)
                    for i, value in enumerate(obj):
                        if isinstance(value, (dict, list)):
                            idx_path = f"{path}[{i}]" if path else f"[{i}]"
                            count += count_items(value, idx_path)
                    return count
                return 0
            
            total_items = count_items(data_snapshot)
            
            # 计算顶级键数量
            if isinstance(data_snapshot, dict):
                top_keys = len(data_snapshot)
            else:
                top_keys = 0
            
            return {
                "data": data_snapshot,
                "metadata": {
                    "total_items": total_items,
                    "top_level_keys": top_keys,
                    "data_type": type(data_snapshot).__name__,
                    "export_timestamp": datetime.now().strftime(
                        "%Y-%m-%d %H:%M:%S")
                }
            }
        except Exception as e:
            logger.error("获取修改后数据时发生错误: %s", e)
            return {"data": self.data_dict, "metadata": {}}
    
    def validate_data(self) -> bool:
        """
        验证当前数据的完整性
        
        Returns:
            bool: 数据是否有效
        """
        try:
            # 加锁后做快照再序列化，避免并发写入导致序列化失败
            import copy
            with self._data_lock:
                snapshot = copy.deepcopy(self.data_dict)
            json.dumps(snapshot, default=str)
            return True
        except Exception as e:
            logger.error("数据验证失败: %s", e)
            return False
    
    def reset_data(self, new_data: Dict[str, Any] = None):
        """
        重置数据到指定状态或初始状态
        
        Args:
            new_data: 新的数据字典，如果为None则使用原始数据
        """
        try:
            if new_data is not None:
                with self._data_lock:
                    self.data_dict.clear()
                    self.data_dict.update(new_data)
            else:
                # 如果有原始数据的备份，恢复到原始状态
                if hasattr(self, 'original_dict') and self.original_dict:
                    with self._data_lock:
                        self.data_dict.clear()
                        self.data_dict.update(self.original_dict)
            
            # 刷新树形显示
            self.populate_tree()
            self.status_label.setText("数据已重置")
            logger.info("数据已重置")
            
        except Exception as e:
            logger.error("重置数据时发生错误: %s", e)
            QMessageBox.warning(self, "错误", f"重置数据失败: {str(e)}")
    
    def get_data_summary(self) -> Dict[str, Any]:
        """
        获取数据概要信息
        
        Returns:
            Dict[str, Any]: 数据概要统计
        """
        try:
            def analyze_structure(obj, depth=0, max_depth=5):
                """分析数据结构"""
                if depth > max_depth:
                    return {"type": "深度限制", "depth": depth}
                
                if isinstance(obj, dict):
                    # 构建字典结构信息
                    items_sample = list(obj.items())[:5]
                    structure = {}
                    for k, v in items_sample:
                        structure[k] = analyze_structure(v, depth+1, max_depth)
                    
                    return {
                        "type": "dict",
                        "length": len(obj),
                        "keys": list(obj.keys())[:10],  # 只显示前10个键
                        "structure": structure  # 只分析前5个项目
                    }
                elif isinstance(obj, list):
                    # 获取列表中元素的类型
                    item_types = []
                    for item in obj[:10]:
                        item_types.append(type(item).__name__)
                    unique_types = list(set(item_types))
                    
                    # 构建列表结构信息
                    structure = []
                    for item in obj[:3]:
                        structure.append(
                            analyze_structure(item, depth+1, max_depth))
                    
                    return {
                        "type": "list",
                        "length": len(obj),
                        "types": unique_types,
                        "structure": structure  # 只分析前3个项目
                    }
                else:
                    # 处理基本类型
                    obj_str = str(obj)
                    if len(obj_str) > 100:
                        display_value = obj_str[:100] + "..."
                    else:
                        display_value = obj_str
                    
                    return {
                        "type": type(obj).__name__,
                        "value": display_value
                    }
            
            return {
                "summary": analyze_structure(self.data_dict),
                "total_size": len(json.dumps(self.data_dict, default=str)),
                "is_valid": self.validate_data()
            }
        except Exception as e:
            logger.error("获取数据概要时发生错误: %s", e)
            return {"error": str(e)}


def show_editable_tree_viewer(data_dict: Dict[str, Any],
                              title: str = "可编辑字典查看器"):
    """显示可编辑的树形查看器"""
    try:
        app = QApplication.instance()
        if app is None:
            app = QApplication(sys.argv)
        
        viewer = EditableTreeViewer(data_dict, title)
        viewer.show()
        
        return viewer
    except Exception as e:
        logger.error("创建树形查看器失败: %s", e)
        return None


# 测试代码
if __name__ == "__main__":
    # 测试数据，包含需要格式化的路径
    test_data = {
        "用户信息": {
            "姓名": "张三",
            "年龄": 25,  # 整数
            "身高": 175.5,  # 浮点数
            "是否已婚": True,  # 布尔值
            "邮箱": "zhangsan@example.com",
            "文档路径": ("E:\\TO程庆宇\\37\\JRS_PDM\\02_项目存档_BU3\\2025\\"
                        "JC250037-数码卷绕机\\01 项目文档\\JC2500370+数码卷绕设备\\"
                        "JC2500370P+P辅助组件\\01 3D模型\\JC250370P.SLDASM"),
            "地址": {
                "省份": "北京市",
                "城市": "北京市",
                "详细地址": "朝阳区某某街道123号",
                "备份路径": "D:\\用户数据\\备份\\2025\\personal_backup.zip"
            }
        },
        "项目配置": {
            "项目名称": "SolidWorks插件",
            "版本": "1.0.0",
            "作者": "开发团队",
            "工作目录": "C:\\Program Files\\SolidWorks\\Plugins\\MyPlugin",
            "日志文件": "C:\\Users\\Admin\\AppData\\Local\\Temp\\plugin.log",
            "端口号": 8080,  # 整数
            "超时时间": 30.5,  # 浮点数
            "启用调试": False,  # 布尔值
            "配置列表": [
                "配置1", 
                "C:\\Config\\config1.json",
                "C:\\Config\\config2.json"
            ]
        },
        "系统设置": {
            "语言": "中文",
            "主题": "默认",
            "日志级别": "INFO",
            "安装路径": "C:\\Program Files (x86)\\MyApp",
            "数据路径": "E:\\ProjectData\\2025\\Current",
            "临时文件夹": "C:\\Windows\\Temp\\MyApp",
            "最大连接数": 100,  # 整数
            "缓存大小": 256.0,  # 浮点数
            "启用缓存": True  # 布尔值
        }
    }
    
    app = QApplication(sys.argv)
    viewer = show_editable_tree_viewer(test_data, "字典编辑器 - 路径格式化测试")
    if viewer:
        sys.exit(app.exec_())



    