import os
from pathlib import Path

from imgui_bundle import imgui, imgui_ctx


class PropertyHelpers:
    """Property, collision, material, and picker helper methods."""

    def __init__(self, app):
        self.app = app

    def __getattr__(self, name):
        return getattr(self.app, name)

    def __setattr__(self, name, value):
        if name == "app" or name in self.__dict__ or hasattr(type(self), name):
            object.__setattr__(self, name, value)
        else:
            setattr(self.app, name, value)


    def _apply_gui_font(self, gui_element, font_path):
            """应用GUI元素的字体"""
            try:
                if hasattr(gui_element, 'setFont') and font_path:
                    gui_element.setFont(font_path)
                gui_element.font_path = font_path
            except Exception as e:
                print(f"应用GUI字体失败: {e}")
        

    def _apply_gui_font_size(self, gui_element, font_size):
        """应用GUI元素的字体大小"""
        try:
            if hasattr(gui_element, 'setFontSize'):
                gui_element.setFontSize(font_size)
            gui_element.font_size = font_size
        except Exception as e:
            print(f"应用GUI字体大小失败: {e}")
    

    def _apply_gui_font_style(self, gui_element):
        """应用GUI元素的字体样式"""
        try:
            if hasattr(gui_element, 'setFontStyle'):
                style = 0
                if getattr(gui_element, 'font_bold', False):
                    style |= 1  # 粗体
                if getattr(gui_element, 'font_italic', False):
                    style |= 2  # 斜体
                gui_element.setFontStyle(style)
        except Exception as e:
            print(f"应用GUI字体样式失败: {e}")
        
        # 特定类型的属性
        if gui_type == "button":
            if imgui.collapsing_header("按钮属性"):
                # 按钮状态
                is_pressed = getattr(gui_element, 'pressed', False)
                changed, new_pressed = imgui.checkbox("按下状态", is_pressed)
                if changed:
                    gui_element.pressed = new_pressed
                
                # 按钮回调
                callback_name = getattr(gui_element, 'callback_name', '')
                changed, new_callback = imgui.input_text("回调函数", callback_name, 64)
                if changed:
                    gui_element.callback_name = new_callback
        
        elif gui_type == "entry":
            if imgui.collapsing_header("输入框属性"):
                # 输入框内容
                entry_text = getattr(gui_element, 'entry_text', '')
                changed, new_text = imgui.input_text("输入内容", entry_text, 256)
                if changed:
                    gui_element.entry_text = new_text
                    if hasattr(gui_element, 'set'):
                        gui_element.set(new_text)
                
                # 最大长度
                max_length = getattr(gui_element, 'max_length', 256)
                changed, new_max = imgui.input_int("最大长度", max_length)
                if changed:
                    gui_element.max_length = max(max_length, 1)
                
                # 密码模式
                is_password = getattr(gui_element, 'is_password', False)
                changed, new_password = imgui.checkbox("密码模式", is_password)
                if changed:
                    gui_element.is_password = new_password
                    if hasattr(gui_element, 'obscure'):
                        gui_element.obscure(new_password)
        
        elif gui_type in ["2d_image", "3d_image"]:
            if imgui.collapsing_header("图像属性"):
                # 图像路径
                image_path = getattr(gui_element, 'image_path', '')
                changed, new_path = imgui.input_text("图像路径", image_path, 256)
                if changed and hasattr(self, 'gui_manager'):
                    gui_element.image_path = new_path
                    # TODO: 重新加载图像
                
                # 图像缩放模式
                scale_mode = getattr(gui_element, 'scale_mode', 'stretch')
                if imgui.begin_combo("缩放模式", scale_mode):
                    if imgui.selectable("拉伸##stretch"):
                        gui_element.scale_mode = 'stretch'
                    if imgui.selectable("适应##fit"):
                        gui_element.scale_mode = 'fit'
                    if imgui.selectable("填充##fill"):
                        gui_element.scale_mode = 'fill'
                    imgui.end_combo()
    

    def _has_collision(self, node):
        """检查节点是否有碰撞体"""
        try:
            if not node or node.isEmpty():
                return False
            
            # 检查是否有碰撞节点
            for child in node.getChildren():
                if hasattr(child, 'getName') and child.getName():
                    name = child.getName()
                    if 'collision' in name.lower() or 'Collision' in name:
                        return True
            
            return False
        except Exception as e:
            print(f"检查碰撞状态失败: {e}")
            return False
    

    def _get_current_collision_shape(self, node):
        """获取当前碰撞形状"""
        try:
            if not self._has_collision(node):
                return "球形 (Sphere)"
            
            # 查找碰撞节点并判断形状
            for child in node.getChildren():
                if hasattr(child, 'getName') and child.getName():
                    name = child.getName()
                    if 'collision' in name.lower() or 'Collision' in name:
                        if hasattr(child.node(), 'getSolids') and child.node().getNumSolids() > 0:
                            from panda3d.core import CollisionSphere, CollisionBox, CollisionCapsule, CollisionPlane
                            solid = child.node().getSolid(0)
                            if isinstance(solid, CollisionSphere):
                                return "球形 (Sphere)"
                            elif isinstance(solid, CollisionBox):
                                return "盒型 (Box)"
                            elif isinstance(solid, CollisionCapsule):
                                return "胶囊体 (Capsule)"
                            elif isinstance(solid, CollisionPlane):
                                return "平面 (Plane)"
                                
                        # 兜底根据名称判断形状
                        if 'sphere' in name.lower():
                            return "球形 (Sphere)"
                        elif 'box' in name.lower():
                            return "盒型 (Box)"
                        elif 'capsule' in name.lower():
                            return "胶囊体 (Capsule)"
                        elif 'plane' in name.lower():
                            return "平面 (Plane)"
            
            return "球形 (Sphere)"  # 默认
        except Exception as e:
            print(f"获取碰撞形状失败: {e}")
            return "球形 (Sphere)"
    

    def _get_current_collision_shape_type(self, node):
        """获取当前碰撞形状类型（内部标识）"""
        try:
            shape_name = self._get_current_collision_shape(node)
            if "Sphere" in shape_name:
                return "sphere"
            elif "Box" in shape_name:
                return "box"
            elif "Capsule" in shape_name:
                return "capsule"
            elif "Plane" in shape_name:
                return "plane"
            else:
                return "sphere"
        except Exception as e:
            print(f"获取碰撞形状类型失败: {e}")
            return "sphere"
    

    def _get_collision_position_offset(self, node):
        """获取碰撞体位置偏移"""
        try:
            if not self._has_collision(node):
                return (0.0, 0.0, 0.0)
            
            # 查找碰撞节点并获取位置
            for child in node.getChildren():
                if hasattr(child, 'getName') and child.getName():
                    name = child.getName()
                    if 'collision' in name.lower() or 'Collision' in name:
                        pos = child.getPos()
                        return (pos.x, pos.y, pos.z)
            
            return (0.0, 0.0, 0.0)
        except Exception as e:
            print(f"获取碰撞位置失败: {e}")
            return (0.0, 0.0, 0.0)
    

    def _is_collision_visible(self, node):
        """检查碰撞体是否可见"""
        try:
            if not self._has_collision(node):
                return False
            
            # 查找碰撞节点并检查可见性
            for child in node.getChildren():
                if hasattr(child, 'getName') and child.getName():
                    name = child.getName()
                    if 'collision' in name.lower() or 'Collision' in name:
                        return child.isHidden() == False
            
            return False
        except Exception as e:
            print(f"检查碰撞可见性失败: {e}")
            return False
    

    def _add_collision_to_node(self, node):
        """为节点添加碰撞体"""
        try:
            if not node or node.isEmpty():
                print("无效的节点")
                return
            
            if self._has_collision(node):
                print("节点已有碰撞体")
                return
            
            # 获取选择的形状
            shape_name = getattr(self.app, '_selected_collision_shape', '球形 (Sphere)')
            
            if hasattr(self, 'collision_manager'):
                # 使用碰撞管理器添加碰撞体
                shape_type = self._get_shape_type_from_name(shape_name)
                collision_node = self.collision_manager.setupAdvancedCollision(
                    node, 
                    shape_type=shape_type, 
                    mask_type='MODEL_COLLISION'
                )
                
                if collision_node:
                    print(f"成功为节点 {node.getName()} 添加 {shape_name} 碰撞体")
                else:
                    print(f"添加碰撞体失败")
            else:
                print("碰撞管理器未初始化")
                
        except Exception as e:
            print(f"添加碰撞体失败: {e}")
            import traceback
            traceback.print_exc()
    

    def _remove_collision_from_node(self, node):
        """从节点移除碰撞体"""
        try:
            if not node or node.isEmpty():
                print("无效的节点")
                return
            
            if not self._has_collision(node):
                print("节点没有碰撞体")
                return
            
            # 查找并移除碰撞节点
            children_to_remove = []
            for child in node.getChildren():
                if hasattr(child, 'getName') and child.getName():
                    name = child.getName()
                    if 'collision' in name.lower() or 'Collision' in name:
                        children_to_remove.append(child)
            
            # 移除找到的碰撞节点
            for child in children_to_remove:
                child.removeNode()
            
            if children_to_remove:
                print(f"成功移除节点 {node.getName()} 的碰撞体")
            else:
                print(f"未找到碰撞体")
                
        except Exception as e:
            print(f"移除碰撞体失败: {e}")
            import traceback
            traceback.print_exc()
    

    def _toggle_collision_visibility(self, node):
        """切换碰撞体可见性"""
        try:
            if not node or node.isEmpty():
                return
            
            # 查找碰撞节点并切换可见性
            for child in node.getChildren():
                if hasattr(child, 'getName') and child.getName():
                    name = child.getName()
                    if 'collision' in name.lower() or 'Collision' in name:
                        if child.isHidden():
                            child.show()
                        else:
                            child.hide()
                        break
                        
        except Exception as e:
            print(f"切换碰撞可见性失败: {e}")
    

    def _update_collision_position(self, node, axis, value):
        """更新碰撞体位置"""
        try:
            if not node or node.isEmpty():
                return
            
            # 查找碰撞节点并更新位置
            for child in node.getChildren():
                if hasattr(child, 'getName') and child.getName():
                    name = child.getName()
                    if 'collision' in name.lower() or 'Collision' in name:
                        current_pos = child.getPos()
                        if axis == 'x':
                            child.setPos(value, current_pos.y, current_pos.z)
                        elif axis == 'y':
                            child.setPos(current_pos.x, value, current_pos.z)
                        elif axis == 'z':
                            child.setPos(current_pos.x, current_pos.y, value)
                        break
                        
        except Exception as e:
            print(f"更新碰撞位置失败: {e}")
    

    def _get_shape_type_from_name(self, shape_name):
        """从形状名称获取形状类型"""
        if "Sphere" in shape_name:
            return "sphere"
        elif "Box" in shape_name:
            return "box"
        elif "Capsule" in shape_name:
            return "capsule"
        elif "Plane" in shape_name:
            return "plane"
        else:
            return "sphere"
    

    def _get_sphere_radius(self, node):
        """获取球形半径"""
        try:
            # 从碰撞节点获取半径信息
            for child in node.getChildren():
                if hasattr(child, 'getName') and child.getName():
                    name = child.getName()
                    if 'collision' in name.lower() or 'Collision' in name:
                        if hasattr(child.node(), 'getSolids') and child.node().getNumSolids() > 0:
                            solid = child.node().getSolid(0)
                            from panda3d.core import CollisionSphere
                            if isinstance(solid, CollisionSphere):
                                return solid.getRadius()
            return 1.0
        except Exception as e:
            print(f"获取球形半径失败: {e}")
            return 1.0
    

    def _update_sphere_radius(self, node, radius):
        """更新球形半径"""
        try:
            # 重新创建碰撞体来更新参数
            if hasattr(self, 'collision_manager'):
                # 先移除旧的碰撞体
                self._remove_collision_from_node(node)
                # 重新创建带有新参数的碰撞体
                self.collision_manager.setupAdvancedCollision(
                    node, 
                    shape_type='sphere', 
                    mask_type='MODEL_COLLISION',
                    radius=radius
                )
                print(f"更新球形半径为: {radius}")
        except Exception as e:
            print(f"更新球形半径失败: {e}")
    

    def _get_box_size(self, node):
        """获取盒型尺寸"""
        try:
            # 从碰撞节点获取尺寸信息
            for child in node.getChildren():
                if hasattr(child, 'getName') and child.getName():
                    name = child.getName()
                    if 'collision' in name.lower() or 'Collision' in name:
                        # 尝试从碰撞体获取尺寸
                        if hasattr(child.node(), 'getSolids') and child.node().getNumSolids() > 0:
                            solid = child.node().getSolid(0)
                            from panda3d.core import CollisionBox
                            if isinstance(solid, CollisionBox):
                                min_p = solid.getMin()
                                max_p = solid.getMax()
                                return (
                                    max_p.x - min_p.x,
                                    max_p.y - min_p.y,
                                    max_p.z - min_p.z
                                )
            return (1.0, 1.0, 1.0)
        except Exception as e:
            print(f"获取盒型尺寸失败: {e}")
            return (1.0, 1.0, 1.0)
    

    def _update_box_size(self, node, axis, value):
        """更新盒型尺寸"""
        try:
            # 获取当前尺寸
            current_size = self._get_box_size(node)
            new_size = list(current_size)
            
            # 更新指定轴的尺寸
            if axis == 'x':
                new_size[0] = value
            elif axis == 'y':
                new_size[1] = value
            elif axis == 'z':
                new_size[2] = value
            
            # 重新创建碰撞体
            if hasattr(self, 'collision_manager'):
                self._remove_collision_from_node(node)
                self.collision_manager.setupAdvancedCollision(
                    node, 
                    shape_type='box', 
                    mask_type='MODEL_COLLISION',
                    width=new_size[0],
                    length=new_size[1],
                    height=new_size[2]
                )
                print(f"更新盒型尺寸: {new_size}")
        except Exception as e:
            print(f"更新盒型尺寸失败: {e}")
    

    def _get_capsule_radius(self, node):
        """获取胶囊体半径"""
        try:
            # 从碰撞节点获取半径信息
            for child in node.getChildren():
                if hasattr(child, 'getName') and child.getName():
                    name = child.getName()
                    if 'collision' in name.lower() or 'Collision' in name:
                        if hasattr(child.node(), 'getSolids') and child.node().getNumSolids() > 0:
                            solid = child.node().getSolid(0)
                            from panda3d.core import CollisionCapsule
                            if isinstance(solid, CollisionCapsule):
                                return solid.getRadius()
            return 1.0
        except Exception as e:
            print(f"获取胶囊体半径失败: {e}")
            return 1.0
    

    def _update_capsule_radius(self, node, radius):
        """更新胶囊体半径"""
        try:
            # 获取当前高度
            height = self._get_capsule_height(node)
            
            # 重新创建碰撞体
            if hasattr(self, 'collision_manager'):
                self._remove_collision_from_node(node)
                self.collision_manager.setupAdvancedCollision(
                    node, 
                    shape_type='capsule', 
                    mask_type='MODEL_COLLISION',
                    radius=radius,
                    height=height
                )
                print(f"更新胶囊体半径为: {radius}")
        except Exception as e:
            print(f"更新胶囊体半径失败: {e}")
    

    def _get_capsule_height(self, node):
        """获取胶囊体高度"""
        try:
            # 从碰撞节点获取高度信息
            for child in node.getChildren():
                if hasattr(child, 'getName') and child.getName():
                    name = child.getName()
                    if 'collision' in name.lower() or 'Collision' in name:
                        if hasattr(child.node(), 'getSolids') and child.node().getNumSolids() > 0:
                            solid = child.node().getSolid(0)
                            from panda3d.core import CollisionCapsule
                            if isinstance(solid, CollisionCapsule):
                                point_a = solid.getPointA()
                                point_b = solid.getPointB()
                                return (point_b - point_a).length() + 2 * solid.getRadius()
            return 2.0
        except Exception as e:
            print(f"获取胶囊体高度失败: {e}")
            return 2.0
    

    def _update_capsule_height(self, node, height):
        """更新胶囊体高度"""
        try:
            # 获取当前半径
            radius = self._get_capsule_radius(node)
            
            # 重新创建碰撞体
            if hasattr(self, 'collision_manager'):
                self._remove_collision_from_node(node)
                self.collision_manager.setupAdvancedCollision(
                    node, 
                    shape_type='capsule', 
                    mask_type='MODEL_COLLISION',
                    radius=radius,
                    height=height
                )
                print(f"更新胶囊体高度为: {height}")
        except Exception as e:
            print(f"更新胶囊体高度失败: {e}")
    

    def _get_plane_normal(self, node):
        """获取平面法向量"""
        try:
            # 从碰撞节点获取法向量信息
            for child in node.getChildren():
                if hasattr(child, 'getName') and child.getName():
                    name = child.getName()
                    if 'collision' in name.lower() or 'Collision' in name:
                        if hasattr(child.node(), 'getSolids') and child.node().getNumSolids() > 0:
                            solid = child.node().getSolid(0)
                            from panda3d.core import CollisionPlane
                            if isinstance(solid, CollisionPlane):
                                plane = solid.getPlane()
                                normal = plane.getNormal()
                                return (normal.x, normal.y, normal.z)
            return (0.0, 0.0, 1.0)
        except Exception as e:
            print(f"获取平面法向量失败: {e}")
            return (0.0, 0.0, 1.0)
    

    def _update_plane_normal(self, node, axis, value):
        """更新平面法向量"""
        try:
            # 获取当前法向量
            current_normal = self._get_plane_normal(node)
            new_normal = list(current_normal)
            
            # 更新指定轴的值
            if axis == 'x':
                new_normal[0] = value
            elif axis == 'y':
                new_normal[1] = value
            elif axis == 'z':
                new_normal[2] = value
            
            # 标准化法向量
            from panda3d.core import Vec3
            normal_vec = Vec3(*new_normal)
            normal_vec.normalize()
            
            # 重新创建碰撞体
            if hasattr(self, 'collision_manager'):
                self._remove_collision_from_node(node)
                self.collision_manager.setupAdvancedCollision(
                    node, 
                    shape_type='plane', 
                    mask_type='MODEL_COLLISION',
                    normal=normal_vec
                )
                print(f"更新平面法向量为: ({normal_vec.x:.2f}, {normal_vec.y:.2f}, {normal_vec.z:.2f})")
        except Exception as e:
            print(f"更新平面法向量失败: {e}")
    

    def _manual_collision_detection(self):
        """手动执行碰撞检测"""
        try:
            if hasattr(self, 'collision_manager'):
                results = self.collision_manager.detectModelCollisions(log_results=True)
                if results:
                    print(f"手动碰撞检测完成，发现 {len(results)} 个碰撞")
                else:
                    print("手动碰撞检测完成，未发现碰撞")
        except Exception as e:
            print(f"手动碰撞检测失败: {e}")

    def _update_node_name(self, node, new_name):
        """更新节点名称"""
        if new_name and new_name != node.getName():
            node.setName(new_name)
            # 更新场景树显示
            if hasattr(self, 'scene_tree'):
                self.scene_tree.refresh()
    

    def _get_material_base_color(self, material):
        """获取材质基础颜色"""
        try:
            if hasattr(material, 'base_color') and material.base_color is not None:
                return (material.base_color.x, material.base_color.y, material.base_color.z, material.base_color.w)
            elif hasattr(material, 'get_base_color'):
                color = material.get_base_color()
                return (color.x, color.y, color.z, color.w)
            elif hasattr(material, 'getDiffuse'):
                color = material.getDiffuse()
                return (color.x, color.y, color.z, color.w if hasattr(color, 'w') else 1.0)
            else:
                return (1.0, 1.0, 1.0, 1.0)  # 默认白色
        except:
            return (1.0, 1.0, 1.0, 1.0)  # 默认白色
    

    def _update_material_base_color(self, material, component, value):
        """更新材质基础颜色"""
        try:
            base_color = self._get_material_base_color(material)
            new_color = list(base_color)
            
            if component == 'r':
                new_color[0] = value
            elif component == 'g':
                new_color[1] = value
            elif component == 'b':
                new_color[2] = value
            elif component == 'a':
                new_color[3] = value
            
            new_color_tuple = tuple(new_color)
            
            if hasattr(material, 'set_base_color'):
                from panda3d.core import Vec4
                material.set_base_color(Vec4(*new_color_tuple))
            elif hasattr(material, 'setDiffuse'):
                from panda3d.core import Vec4
                material.setDiffuse(Vec4(*new_color_tuple))
        except Exception as e:
            print(f"更新材质基础颜色失败: {e}")
    

    def _update_material_roughness(self, material, value):
        """更新材质粗糙度"""
        try:
            if hasattr(material, 'set_roughness'):
                material.set_roughness(value)
        except Exception as e:
            print(f"更新材质粗糙度失败: {e}")
    

    def _update_material_metallic(self, material, value):
        """更新材质金属性"""
        try:
            if hasattr(material, 'set_metallic'):
                material.set_metallic(value)
        except Exception as e:
            print(f"更新材质金属性失败: {e}")
    

    def _update_material_ior(self, material, value):
        """更新材质折射率"""
        try:
            if hasattr(material, 'set_refractive_index'):
                material.set_refractive_index(value)
        except Exception as e:
            print(f"更新材质折射率失败: {e}")
    

    def _apply_material_preset(self, material, preset_name):
        """应用材质预设"""
        try:
            from panda3d.core import Vec4, Material
            
            presets = {
                "默认": {
                    "base_color": Vec4(0.8, 0.8, 0.8, 1.0),
                    "roughness": 0.5,
                    "metallic": 0.0,
                    "ior": 1.5
                },
                "金属": {
                    "base_color": Vec4(0.7, 0.7, 0.8, 1.0),
                    "roughness": 0.2,
                    "metallic": 1.0,
                    "ior": 2.5
                },
                "塑料": {
                    "base_color": Vec4(0.9, 0.9, 0.9, 1.0),
                    "roughness": 0.8,
                    "metallic": 0.0,
                    "ior": 1.45
                },
                "玻璃": {
                    "base_color": Vec4(0.9, 0.9, 1.0, 0.2),
                    "roughness": 0.0,
                    "metallic": 0.0,
                    "ior": 1.5
                },
                "木材": {
                    "base_color": Vec4(0.6, 0.4, 0.2, 1.0),
                    "roughness": 0.9,
                    "metallic": 0.0,
                    "ior": 1.55
                },
                "混凝土": {
                    "base_color": Vec4(0.5, 0.5, 0.5, 1.0),
                    "roughness": 1.0,
                    "metallic": 0.0,
                    "ior": 1.5
                }
            }
            
            if preset_name in presets:
                preset = presets[preset_name]
                
                # 应用基础颜色
                if hasattr(material, 'set_base_color'):
                    material.set_base_color(preset["base_color"])
                elif hasattr(material, 'setDiffuse'):
                    material.setDiffuse(preset["base_color"])
                
                # 应用PBR属性
                if hasattr(material, 'set_roughness'):
                    material.set_roughness(preset["roughness"])
                if hasattr(material, 'set_metallic'):
                    material.set_metallic(preset["metallic"])
                if hasattr(material, 'set_refractive_index'):
                    material.set_refractive_index(preset["ior"])
                
                print(f"已应用材质预设: {preset_name}")
        except Exception as e:
            print(f"应用材质预设失败: {e}")
    

    def _apply_material_to_node(self, node):
        """为节点应用材质"""
        try:
            from panda3d.core import Material, Vec4
            
            # 检查是否已有材质
            materials = node.find_all_materials()
            
            if not materials:
                # 创建新材质
                material = Material(f"default-material-{node.getName()}")
                material.setBaseColor(Vec4(0.8, 0.8, 0.8, 1.0))
                material.setDiffuse(Vec4(0.8, 0.8, 0.8, 1.0))
                material.setAmbient(Vec4(0.4, 0.4, 0.4, 1.0))
                material.setSpecular(Vec4(0.1, 0.1, 0.1, 1.0))
                material.setShininess(10.0)
                node.setMaterial(material, 1)
                print(f"已为新节点创建默认材质")
            else:
                print(f"节点已有 {len(materials)} 个材质")
        except Exception as e:
            print(f"应用材质失败: {e}")
    

    def _reset_material(self, node):
        """重置节点材质"""
        try:
            materials = node.find_all_materials()
            
            for material in materials:
                # 重置为默认材质属性
                from panda3d.core import Vec4
                default_color = Vec4(0.8, 0.8, 0.8, 1.0)
                
                if hasattr(material, 'set_base_color'):
                    material.set_base_color(default_color)
                elif hasattr(material, 'setDiffuse'):
                    material.setDiffuse(default_color)
                
                if hasattr(material, 'set_roughness'):
                    material.set_roughness(0.5)
                if hasattr(material, 'set_metallic'):
                    material.set_metallic(0.0)
                if hasattr(material, 'set_refractive_index'):
                    material.set_refractive_index(1.5)
            
            print(f"已重置材质")
        except Exception as e:
            print(f"重置材质失败: {e}")
    

    def _select_texture_for_material(self, node, material, texture_type):
        """为材质选择纹理"""
        try:
            import tkinter as tk
            from tkinter import filedialog

            # 推断初始目录
            initial_dir = getattr(self, "_texture_dialog_path", "")
            if not initial_dir or not os.path.exists(initial_dir):
                candidates = [
                    os.path.join(os.getcwd(), "Resources", "textures"),
                    os.path.join(os.getcwd(), "Resources"),
                    os.getcwd(),
                ]
                initial_dir = next((p for p in candidates if os.path.exists(p)), os.getcwd())

            # 弹出系统文件选择窗口
            root = tk.Tk()
            root.withdraw()
            try:
                root.attributes("-topmost", True)
            except Exception:
                pass

            selected_path = filedialog.askopenfilename(
                title=f"选择{texture_type}贴图",
                initialdir=initial_dir,
                filetypes=[
                    ("Image Files", "*.png;*.jpg;*.jpeg;*.bmp;*.tif;*.tiff;*.tga;*.dds;*.ktx;*.hdr;*.exr"),
                    ("All Files", "*.*"),
                ],
            )
            root.destroy()

            if selected_path and os.path.exists(selected_path):
                self._texture_dialog_path = os.path.dirname(selected_path)
                self._apply_texture_to_material(node, material, texture_type, selected_path)
            else:
                print(f"已取消选择{texture_type}贴图")

        except Exception as e:
            print(f"选择纹理失败: {e}")
    

    def _get_material_texture_slots(self):
        """材质贴图类型到固定槽位映射（对应 p3d_TextureN）"""
        return {
            "diffuse": 0,    # p3d_Texture0
            "normal": 1,     # p3d_Texture1
            "ior": 2,        # p3d_Texture2
            "roughness": 3,  # p3d_Texture3
            "parallax": 4,   # p3d_Texture4
            "metallic": 5,   # p3d_Texture5
            "emission": 6,   # p3d_Texture6
            "ao": 7,         # p3d_Texture7
            "alpha": 8,      # p3d_Texture8
            "detail": 9,     # p3d_Texture9
            "gloss": 10      # p3d_Texture10
        }


    def _get_material_stage_mode_map(self):
        """不同贴图类型对应的 TextureStage 模式"""
        from panda3d.core import TextureStage
        return {
            "diffuse": TextureStage.MModulate,
            "normal": TextureStage.MNormal,
            "roughness": TextureStage.MGloss,
            "metallic": TextureStage.MSelector,
            "emission": TextureStage.MGlow,
            "ao": TextureStage.MSelector,
            "alpha": TextureStage.MBlend,
            "parallax": TextureStage.MHeight,
            "detail": TextureStage.MModulate,
            "gloss": TextureStage.MGloss,
            "ior": TextureStage.MSelector,
        }


    def _find_texture_stage_by_name(self, node, stage_name):
        """按名称查找节点上的纹理阶段"""
        texture_stages = node.findAllTextureStages()
        for i in range(texture_stages.getNumTextureStages()):
            stage = texture_stages.getTextureStage(i)
            if stage and stage.getName() == stage_name:
                return stage
        return None


    def _get_neutral_texture(self, texture_type):
        """获取指定贴图类型的中性占位纹理（1x1）"""
        try:
            from panda3d.core import Texture

            if not hasattr(self, "_neutral_texture_cache"):
                self._neutral_texture_cache = {}
            if texture_type in self._neutral_texture_cache:
                return self._neutral_texture_cache[texture_type]

            # 各类型中性值，确保在未显式设置该通道时不影响结果
            neutral_rgba = {
                "diffuse": (255, 255, 255, 255),   # 白色，不改变底色
                "normal": (128, 128, 255, 255),    # 平面法线
                "ior": (0, 0, 0, 255),             # blend_ior 中的“无附加影响”值
                "roughness": (255, 255, 255, 255), # 1.0
                "parallax": (0, 0, 0, 255),        # 0 高度
                "metallic": (255, 255, 255, 255),  # 1.0，保持乘法中性
                "emission": (0, 0, 0, 255),        # 无自发光
                "ao": (255, 255, 255, 255),        # 1.0，不衰减
                "alpha": (255, 255, 255, 255),     # 完全不透明
                "detail": (255, 255, 255, 255),    # 不改变细节叠加
                "gloss": (0, 0, 0, 255),           # 0 光泽（对 roughness 影响最小）
            }

            rgba = neutral_rgba.get(texture_type, (255, 255, 255, 255))
            tex = Texture(f"__neutral_{texture_type}")
            tex.setup2dTexture(1, 1, Texture.T_unsigned_byte, Texture.F_rgba8)
            tex.setRamImage(bytes(rgba))
            tex.setMagfilter(Texture.FTNearest)
            tex.setMinfilter(Texture.FTNearest)

            self._neutral_texture_cache[texture_type] = tex
            return tex
        except Exception as e:
            print(f"创建中性纹理失败({texture_type}): {e}")
            return None


    def _ensure_texture_slot_alignment(self, node, target_slot, texture_slots, stage_mode_map):
        """补齐低位槽的占位纹理，确保 p3d_TextureN 与固定槽位一致"""
        try:
            from panda3d.core import TextureStage
        except Exception:
            return

        slot_to_type = {slot: tex_type for tex_type, slot in texture_slots.items()}
        for required_slot in range(target_slot):
            required_type = slot_to_type.get(required_slot)
            if not required_type:
                continue

            stage_name = f"{required_type}_map"
            if self._find_texture_stage_by_name(node, stage_name):
                continue

            neutral_texture = self._get_neutral_texture(required_type)
            if not neutral_texture:
                continue

            stage = TextureStage(stage_name)
            stage.setSort(required_slot)
            stage.setMode(stage_mode_map.get(required_type, TextureStage.MSelector))
            node.setTexture(stage, neutral_texture, 1)


    def _ensure_metallic_texture_effect(self, node):
        """启用支持 p3d_Texture5 的 effect，确保金属性贴图生效"""
        try:
            if not node or node.isEmpty():
                return

            render_pipeline = getattr(self, "render_pipeline", None)
            if not render_pipeline:
                return

            # 避免重复设置 effect
            if node.hasTag("material_effect_metallic_enabled"):
                return

            render_pipeline.set_effect(
                node,
                "effects/pbr_with_metallic.yaml",
                {
                    "normal_mapping": True,
                    "render_gbuffer": True,
                    "alpha_testing": False,
                    "parallax_mapping": False,
                    "render_shadow": True,
                    "render_envmap": True
                },
                60
            )
            node.setTag("material_effect_metallic_enabled", "1")
        except Exception as e:
            print(f"启用金属性贴图 effect 失败: {e}")


    def _ensure_default_texture_effect(self, node, enable_parallax=False):
        """启用默认贴图 effect（主要用于法线/粗糙度等通道）"""
        try:
            if not node or node.isEmpty():
                return

            render_pipeline = getattr(self, "render_pipeline", None)
            if not render_pipeline:
                return

            # 已启用金属性增强 effect 时，不覆盖它
            if node.hasTag("material_effect_metallic_enabled"):
                return

            parallax_enabled = enable_parallax or node.hasTag("material_effect_parallax_enabled")
            if parallax_enabled:
                node.setTag("material_effect_parallax_enabled", "1")

            # 为了避免因未知默认值导致 normal mapping 关闭，这里显式设置
            render_pipeline.set_effect(
                node,
                "effects/default.yaml",
                {
                    "normal_mapping": True,
                    "render_gbuffer": True,
                    "alpha_testing": False,
                    "parallax_mapping": parallax_enabled,
                    "render_shadow": True,
                    "render_envmap": True
                },
                55
            )
            node.setTag("material_effect_default_texture_enabled", "1")
        except Exception as e:
            print(f"启用默认贴图 effect 失败: {e}")


    def _set_material_scalar_property(self, material, prop_name, value):
        """安全设置材质标量属性（兼容 snake_case / CamelCase）"""
        try:
            if not material:
                return False

            setter_map = {
                "roughness": ("set_roughness", "setRoughness"),
                "metallic": ("set_metallic", "setMetallic"),
                "ior": ("set_refractive_index", "setRefractiveIndex"),
            }
            setters = setter_map.get(prop_name, ())
            for setter_name in setters:
                if hasattr(material, setter_name):
                    getattr(material, setter_name)(value)
                    return True

            if hasattr(material, prop_name):
                setattr(material, prop_name, value)
                return True
        except Exception as e:
            print(f"设置材质属性失败({prop_name}={value}): {e}")
        return False


    def _get_material_emission(self, material):
        """安全获取材质发光向量"""
        try:
            if not material:
                return None
            if hasattr(material, "emission") and material.emission is not None:
                return material.emission
            if hasattr(material, "get_emission"):
                return material.get_emission()
            if hasattr(material, "getEmission"):
                return material.getEmission()
        except Exception:
            return None
        return None


    def _set_material_emission(self, material, emission_vec):
        """安全设置材质发光向量"""
        try:
            if not material:
                return False
            if hasattr(material, "set_emission"):
                material.set_emission(emission_vec)
                return True
            if hasattr(material, "setEmission"):
                material.setEmission(emission_vec)
                return True
            if hasattr(material, "emission"):
                material.emission = emission_vec
                return True
        except Exception as e:
            print(f"设置材质发光参数失败: {e}")
        return False


    def _configure_material_for_texture_map(self, material, texture_type):
        """贴图驱动参数修正：避免必须手动把数值调到 1.0 才生效"""
        try:
            from panda3d.core import Vec4

            if not material:
                return

            if texture_type == "roughness":
                self._set_material_scalar_property(material, "roughness", 1.0)
            elif texture_type == "metallic":
                # 默认 metallic=0 会把金属贴图乘没，设置为 1 让贴图直接驱动
                self._set_material_scalar_property(material, "metallic", 1.0)
            elif texture_type == "normal":
                # RenderPipeline 中 normalfactor 映射到 emission.y
                emission = self._get_material_emission(material)
                if emission is None:
                    emission = Vec4(0.0, 0.0, 0.0, 1.0)

                new_emission = Vec4(
                    float(getattr(emission, "x", 0.0)),
                    1.0,
                    float(getattr(emission, "z", 0.0)),
                    float(getattr(emission, "w", 1.0)),
                )
                self._set_material_emission(material, new_emission)
        except Exception as e:
            print(f"修正贴图驱动参数失败({texture_type}): {e}")


    def _apply_texture_to_material(self, node, material, texture_type, texture_path):
        """应用纹理到材质"""
        try:
            import os
            from panda3d.core import TextureStage, Filename

            # 加载纹理（优先使用应用的 loader，避免创建临时 Loader 导致状态不一致）
            loader = getattr(self, "loader", None)
            if not loader:
                print("无法加载纹理: loader 不可用")
                return

            # 关键修复：把系统路径统一转换成 Panda3D 可识别格式（避免 D:/ 与 /d/ 冲突）
            normalized_path = texture_path
            if texture_path:
                try:
                    from scene import util
                    normalized_path = util.normalize_model_path(texture_path)
                except Exception:
                    # 在 scene 模块初始化阶段可能出现循环导入，回退到 Panda3D 标准路径
                    normalized_path = Filename.from_os_specific(texture_path).get_fullpath()
            panda_filename = Filename.from_os_specific(texture_path) if texture_path else None

            texture = None
            candidate_paths = []
            if normalized_path:
                candidate_paths.append(normalized_path)
            if panda_filename:
                candidate_paths.append(panda_filename)
                candidate_paths.append(panda_filename.get_fullpath())
            if texture_path:
                candidate_paths.append(texture_path)

            for path_candidate in candidate_paths:
                if not path_candidate:
                    continue
                try:
                    texture = loader.loadTexture(path_candidate)
                    if texture:
                        break
                except Exception:
                    continue
            
            if not texture:
                print(f"无法加载纹理: {texture_path} (normalized={normalized_path})")
                return
            
            # 设置纹理属性
            texture.setMagfilter(texture.FTLinear)
            texture.setMinfilter(texture.FTLinearMipmapLinear)
            
            texture_type = (texture_type or "").strip().lower()
            texture_slots = self._get_material_texture_slots()
            if texture_type not in texture_slots:
                print(f"未知纹理类型: {texture_type}")
                return

            slot = texture_slots[texture_type]

            # 纹理阶段名保持稳定，便于重复设置时精确替换
            stage_name = f"{texture_type}_map"

            # 修正材质参数，确保贴图不需要手工把滑块调到 1.0 才可见
            self._configure_material_for_texture_map(material, texture_type)

            # 清理同类型旧 stage，避免叠加造成覆盖/污染
            old_stage = self._find_texture_stage_by_name(node, stage_name)
            if old_stage:
                node.clearTexture(old_stage)

            # 关键修复：补齐低位槽，避免 p3d_TextureN 因“缺槽”而错位
            stage_mode_map = self._get_material_stage_mode_map()
            self._ensure_texture_slot_alignment(node, slot, texture_slots, stage_mode_map)

            texture_stage = TextureStage(stage_name)
            texture_stage.setSort(slot)
            texture_stage.setMode(stage_mode_map.get(texture_type, TextureStage.MSelector))

            # 应用 stage（用于可视状态与序列化）
            node.setTexture(texture_stage, texture, 1)

            # 同时写入 shader 输入，进一步保证固定槽位可直接读取
            node.setShaderInput(f"p3d_Texture{slot}", texture)

            # 金属性贴图需要额外 effect 才会被默认管线采样
            if texture_type == "metallic":
                self._ensure_metallic_texture_effect(node)
            else:
                # 法线贴图依赖 normal_mapping 宏，显式开启默认 effect
                self._ensure_default_texture_effect(node, enable_parallax=(texture_type == "parallax"))

            # 记录路径，便于 UI 展示和后续恢复
            if texture_path:
                node.setTag(f"material_texture_{texture_type}", os.path.normpath(normalized_path or texture_path))
            
            print(f"已应用{texture_type}纹理到槽位 p3d_Texture{slot}: {texture_path}")
            
        except Exception as e:
            print(f"应用纹理失败: {e}")
    

    def _clear_all_textures(self, node):
        """清除节点所有纹理"""
        try:
            # 清除所有纹理阶段
            node.clearTexture()

            # 清理额外 shader input 与记录标签
            texture_slots = self._get_material_texture_slots()
            for texture_type, slot in texture_slots.items():
                try:
                    node.clearShaderInput(f"p3d_Texture{slot}")
                except Exception:
                    pass
                tag_name = f"material_texture_{texture_type}"
                if node.hasTag(tag_name):
                    node.clearTag(tag_name)
            if node.hasTag("material_effect_metallic_enabled"):
                node.clearTag("material_effect_metallic_enabled")
            if node.hasTag("material_effect_default_texture_enabled"):
                node.clearTag("material_effect_default_texture_enabled")
            if node.hasTag("material_effect_parallax_enabled"):
                node.clearTag("material_effect_parallax_enabled")

            print("已清除所有纹理")
        except Exception as e:
            print(f"清除纹理失败: {e}")
    

    def _display_current_textures(self, node, material):
        """显示当前纹理信息"""
        try:
            # 获取所有纹理阶段
            texture_stages = node.findAllTextureStages()
            
            has_any = False

            if texture_stages:
                imgui.text("当前纹理:")
                for i in range(texture_stages.getNumTextureStages()):
                    stage = texture_stages.getTextureStage(i)
                    texture = node.getTexture(stage)
                    if texture:
                        stage_name = stage.getName() or "未命名"
                        # 隐藏未被用户显式设置的占位纹理，避免面板误导
                        if stage_name.endswith("_map"):
                            tex_type = stage_name[:-4]
                            if tex_type and not node.hasTag(f"material_texture_{tex_type}"):
                                continue

                        texture_name = texture.getName() or "未命名"
                        imgui.text(f"  {stage_name}: {texture_name}")
                        has_any = True

            # 显示属性面板记录的贴图路径（包含非漫反射通道）
            tracked_types = ["diffuse", "normal", "roughness", "metallic", "emission", "ao", "alpha", "parallax", "detail", "gloss", "ior"]
            for texture_type in tracked_types:
                tag_name = f"material_texture_{texture_type}"
                if node.hasTag(tag_name):
                    imgui.text(f"  {texture_type}: {node.getTag(tag_name)}")
                    has_any = True

            if not has_any:
                imgui.text_colored((0.7, 0.7, 0.7, 1.0), "当前无纹理")
        except Exception as e:
            print(f"显示纹理信息失败: {e}")
    

    def _update_shading_model(self, material, model_index):
        """更新着色模型"""
        try:
            from panda3d.core import Vec4
            
            # 根据不同的着色模型设置相应的参数
            if model_index == 1:  # 自发光着色模型
                print("设置自发光着色模型...")
                if hasattr(material, 'set_emission'):
                    current_emission = material.emission or Vec4(0, 0, 0, 0)
                    new_emission = Vec4(1.0, current_emission.y, current_emission.z, current_emission.w)
                    material.set_emission(new_emission)
                print("自发光着色模型设置完成")
            
            elif model_index == 3:  # 透明着色模型
                print("设置透明着色模型...")
                if hasattr(material, 'set_emission'):
                    current_emission = material.emission or Vec4(0, 0, 0, 0)
                    new_emission = Vec4(3.0, 0, 0, 0)  # 3表示透明着色模型
                    material.set_emission(new_emission)
                    
                    # 设置默认透明度
                    if hasattr(material, 'shading_model_param0'):
                        material.shading_model_param0 = 0.8  # 默认80%不透明度
                
                print("透明着色模型设置完成")
            
            else:  # 默认着色模型
                print("设置默认着色模型...")
                if hasattr(material, 'set_emission'):
                    current_emission = material.emission or Vec4(0, 0, 0, 0)
                    new_emission = Vec4(0.0, current_emission.y, current_emission.z, current_emission.w)
                    material.set_emission(new_emission)
                print("默认着色模型设置完成")
            
            print(f"着色模型已更新为: {model_index} ({'自发光' if model_index == 1 else '透明' if model_index == 3 else '默认'})")
        
        except Exception as e:
            print(f"更新着色模型失败: {e}")
    

    def _update_transparency(self, material, opacity_value):
        """更新透明度"""
        try:
            if hasattr(material, 'shading_model_param0'):
                material.shading_model_param0 = opacity_value
                print(f"透明度已更新: {opacity_value}")
        except Exception as e:
            print(f"更新透明度失败: {e}")
    

    def _draw_texture_file_dialog(self):
        """系统文件对话框模式下无需绘制 ImGui 贴图对话框"""
        return

    def start_transform_monitoring(self, node):
        """开始变换监控"""
        if node and not node.isEmpty():
            self._monitored_node = node
            self._transform_monitoring = True
            self._transform_update_timer = 0
            
            # 记录初始变换值
            self._update_last_transform_values()
    

    def stop_transform_monitoring(self):
        """停止变换监控"""
        self._transform_monitoring = False
        self._monitored_node = None
        self._last_transform_values = {}
    

    def _update_last_transform_values(self):
        """更新最后记录的变换值"""
        if self._monitored_node and not self._monitored_node.isEmpty():
            try:
                pos = self._monitored_node.getPos()
                hpr = self._monitored_node.getHpr()
                scale = self._monitored_node.getScale()
                
                self._last_transform_values = {
                    'pos': (pos.x, pos.y, pos.z),
                    'hpr': (hpr.x, hpr.y, hpr.z),
                    'scale': (scale.x, scale.y, scale.z)
                }
            except Exception as e:
                print(f"更新变换值失败: {e}")
    

    def _check_transform_changes(self):
        """检查变换变化"""
        if not self._transform_monitoring or not self._monitored_node:
            return
        
        try:
            pos = self._monitored_node.getPos()
            hpr = self._monitored_node.getHpr()
            scale = self._monitored_node.getScale()
            
            current_values = {
                'pos': (pos.x, pos.y, pos.z),
                'hpr': (hpr.x, hpr.y, hpr.z),
                'scale': (scale.x, scale.y, scale.z)
            }
            
            # 检查是否有变化
            if current_values != self._last_transform_values:
                # 更新记录的值
                self._last_transform_values = current_values
                # 触发属性面板更新（通过设置更新标志）
                self.property_panel_update_timer = 0
                
        except Exception as e:
            print(f"检查变换变化失败: {e}")
    

    def update_transform_monitoring(self, dt):
        """更新变换监控（在主循环中调用）"""
        if not self._transform_monitoring:
            return
        
        self._transform_update_timer += dt
        if self._transform_update_timer >= self._transform_update_interval:
            self._transform_update_timer = 0
            self._check_transform_changes()
    

    def show_color_picker(self, target_object, property_name, initial_color, callback=None):
        """显示颜色选择器"""
        self._color_picker_active = True
        self._color_picker_target = (target_object, property_name)
        self._color_picker_current_color = initial_color
        self._color_picker_callback = callback
    

    def _draw_color_picker(self):
        """绘制颜色选择器对话框"""
        if not self._color_picker_active:
            return
        
        # 设置对话框标志
        flags = (imgui.WindowFlags_.no_resize | 
                imgui.WindowFlags_.no_collapse |
                imgui.WindowFlags_.modal)
        
        # 获取屏幕尺寸，居中显示对话框
        display_size = imgui.get_io().display_size
        dialog_width = 300
        dialog_height = 400
        imgui.set_next_window_size((dialog_width, dialog_height))
        imgui.set_next_window_pos(
            ((display_size.x - dialog_width) / 2, (display_size.y - dialog_height) / 2)
        )
        
        with imgui_ctx.begin("颜色选择器", True, flags) as window:
            if not window.opened:
                self._color_picker_active = False
                self._color_picker_target = None
                return
            
            imgui.text("选择颜色")
            imgui.separator()
            
            # 颜色编辑器
            changed, new_color = imgui.color_edit4(
                "颜色##color_picker", 
                self._color_picker_current_color
            )
            
            if changed:
                self._color_picker_current_color = new_color
            
            # 预设颜色
            imgui.text("预设颜色")
            preset_colors = [
                (1.0, 0.0, 0.0, 1.0),  # 红色
                (0.0, 1.0, 0.0, 1.0),  # 绿色
                (0.0, 0.0, 1.0, 1.0),  # 蓝色
                (1.0, 1.0, 0.0, 1.0),  # 黄色
                (1.0, 0.0, 1.0, 1.0),  # 洋红
                (0.0, 1.0, 1.0, 1.0),  # 青色
                (1.0, 1.0, 1.0, 1.0),  # 白色
                (0.0, 0.0, 0.0, 1.0),  # 黑色
                (0.5, 0.5, 0.5, 1.0),  # 灰色
                (0.188, 0.404, 0.753, 1.0),  # 主题蓝色
                (0.176, 1.0, 0.769, 1.0),  # 主题绿色
                (0.953, 0.616, 0.471, 1.0),  # 主题橙色
            ]
            
            # 绘制预设颜色按钮
            colors_per_row = 6
            for i, color in enumerate(preset_colors):
                if i % colors_per_row != 0:
                    imgui.same_line()
                
                imgui.color_button(f"preset_{i}", color, 0, (30, 30))
                if imgui.is_item_clicked():
                    self._color_picker_current_color = color
            
            imgui.separator()
            
            # 按钮区域
            if imgui.button("确定"):
                self._apply_color_selection()
                self._color_picker_active = False
                self._color_picker_target = None
            
            imgui.same_line()
            if imgui.button("取消"):
                self._color_picker_active = False
                self._color_picker_target = None
    

    def _apply_color_selection(self):
        """应用颜色选择"""
        if not self._color_picker_target:
            return
        
        target_object, property_name = self._color_picker_target
        
        try:
            # 应用颜色到目标对象
            if hasattr(target_object, 'setColor'):
                target_object.setColor(self._color_picker_current_color)
            elif hasattr(target_object, property_name):
                setattr(target_object, property_name, self._color_picker_current_color)
            
            # 调用回调函数
            if self._color_picker_callback:
                self._color_picker_callback(self._color_picker_current_color)
                
        except Exception as e:
            print(f"应用颜色失败: {e}")
    

    def _draw_color_button(self, label, color, size=(50, 20)):
        """绘制颜色按钮并支持点击打开颜色选择器"""
        imgui.color_button(label, color, 0, size)
        if imgui.is_item_clicked():
            # 打开颜色选择器
            self.show_color_picker(None, None, color)
    

    def _refresh_available_fonts(self):
        """刷新可用字体列表"""
        try:
            import platform
            from pathlib import Path
            
            system = platform.system().lower()
            font_paths = []
            
            if system == "linux":
                font_dirs = [
                    "/usr/share/fonts/truetype/",
                    "/usr/share/fonts/opentype/",
                    "/usr/local/share/fonts/",
                    "~/.fonts/"
                ]
            elif system == "windows":
                font_dirs = [
                    "C:/Windows/Fonts/",
                ]
            elif system == "darwin":
                font_dirs = [
                    "/System/Library/Fonts/",
                    "/Library/Fonts/",
                    "~/Library/Fonts/"
                ]
            else:
                font_dirs = []
            
            # 扫描字体目录
            common_fonts = []
            for font_dir in font_dirs:
                font_path = Path(font_dir).expanduser()
                if font_path.exists():
                    for font_file in font_path.rglob("*.ttf"):
                        common_fonts.append(str(font_file))
                    for font_file in font_path.rglob("*.otf"):
                        common_fonts.append(str(font_file))
                    for font_file in font_path.rglob("*.ttc"):
                        common_fonts.append(str(font_file))
            
            # 过滤常见字体
            font_keywords = [
                "arial", "helvetica", "times", "courier", "verdana", "georgia",
                "comic", "impact", "trebuchet", "palatino", "garamond",
                "noto", "dejavu", "liberation", "ubuntu", "roboto", "open",
                "droid", "source", "wenquanyi", "wqy", "pingfang", "stheiti",
                "microsoft", "msyh", "simsun", "simhei", "kaiti", "fangsong"
            ]
            
            self._available_fonts = []
            for font_path in common_fonts:
                font_name = Path(font_path).name.lower()
                if any(keyword in font_name for keyword in font_keywords):
                    self._available_fonts.append(font_path)
            
            # 添加一些默认字体路径
            default_fonts = [
                "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
                "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
                "/usr/share/fonts/opentype/noto/NotoSans-Regular.ttf",
                "C:/Windows/Fonts/arial.ttf",
                "C:/Windows/Fonts/msyh.ttc",
                "/System/Library/Fonts/PingFang.ttc"
            ]
            
            for font_path in default_fonts:
                if Path(font_path).exists() and font_path not in self._available_fonts:
                    self._available_fonts.append(font_path)
            
            print(f"✓ 找到 {len(self._available_fonts)} 个可用字体")
            
        except Exception as e:
            print(f"⚠ 字体扫描失败: {e}")
            self._available_fonts = []
    

    def show_font_selector(self, target_object, property_name, current_font, callback=None):
        """显示字体选择器"""
        self._font_selector_active = True
        self._font_selector_target = (target_object, property_name)
        self._font_selector_current_font = current_font or ""
        self._font_selector_callback = callback
    

    def _draw_font_selector(self):
        """绘制字体选择器对话框"""
        if not self._font_selector_active:
            return
        
        # 设置对话框标志
        flags = (imgui.WindowFlags_.no_resize | 
                imgui.WindowFlags_.no_collapse |
                imgui.WindowFlags_.modal)
        
        # 获取屏幕尺寸，居中显示对话框
        display_size = imgui.get_io().display_size
        dialog_width = 400
        dialog_height = 500
        imgui.set_next_window_size((dialog_width, dialog_height))
        imgui.set_next_window_pos(
            ((display_size.x - dialog_width) / 2, (display_size.y - dialog_height) / 2)
        )
        
        with imgui_ctx.begin("字体选择器", True, flags) as window:
            if not window.opened:
                self._font_selector_active = False
                self._font_selector_target = None
                return
            
            imgui.text("选择字体")
            imgui.separator()
            
            # 当前字体显示
            imgui.text(f"当前字体: {self._font_selector_current_font or '默认'}")
            
            # 字体搜索框
            changed, search_text = imgui.input_text("搜索", "", 256)
            imgui.separator()
            
            # 字体列表
            if imgui.begin_child("font_list", (380, 300)):
                for font_path in self._available_fonts:
                    font_name = Path(font_path).name
                    
                    # 搜索过滤
                    if search_text and search_text.lower() not in font_name.lower():
                        continue
                    
                    # 字体项
                    if imgui.selectable(font_name, font_path == self._font_selector_current_font):
                        self._font_selector_current_font = font_path
                    
                    # 显示字体路径作为工具提示
                    if imgui.is_item_hovered():
                        imgui.set_tooltip(font_path)
            
            imgui.end_child()
            
            imgui.separator()
            
            # 按钮区域
            if imgui.button("确定"):
                self._apply_font_selection()
                self._font_selector_active = False
                self._font_selector_target = None
            
            imgui.same_line()
            if imgui.button("取消"):
                self._font_selector_active = False
                self._font_selector_target = None
            
            imgui.same_line()
            if imgui.button("刷新字体"):
                self._refresh_available_fonts()
    

    def _apply_font_selection(self):
        """应用字体选择"""
        if not self._font_selector_target:
            return
        
        target_object, property_name = self._font_selector_target
        
        try:
            # 应用字体到目标对象
            if hasattr(target_object, property_name):
                setattr(target_object, property_name, self._font_selector_current_font)
            
            # 调用回调函数
            if self._font_selector_callback:
                self._font_selector_callback(self._font_selector_current_font)
                
        except Exception as e:
            print(f"应用字体失败: {e}")
    

    def _draw_font_selector_button(self, label, current_font):
        """绘制字体选择器按钮"""
        font_name = Path(current_font).name if current_font else "默认字体"
        display_text = f"{font_name[:20]}..." if len(font_name) > 20 else font_name
        
        if imgui.button(f"{label}: {display_text}##font_selector"):
            self.show_font_selector(None, None, current_font)
