from panda3d.core import loadPrcFileData, WindowProperties, Point3
from math import pi, sin, cos

from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import Sequence
from panda3d.core import NodePath

import p3dimgui

from imgui_bundle import imgui, imgui_ctx

import sys
import os
import warnings
from pathlib import Path

# 导入MyWorld类和必要的模块
from core.world import CoreWorld
from core.selection import SelectionSystem
from core.event_handler import EventHandler
from core.tool_manager import ToolManager
from core.script_system import ScriptManager
from core.Command_System import CommandManager
from core.terrain_manager import TerrainManager
from scene.scene_manager import SceneManager
from project.project_manager import ProjectManager
from core.collision_manager import CollisionManager
from core.CustomMouseController import CustomMouseController
from core.resource_manager import ResourceManager
from ui.lui_manager import LUIManager
from ui.panels.editor_panels import EditorPanels
from ui.panels.script_panels import ScriptPanels
from ui.panels.dialog_panels import DialogPanels
from ui.panels.interaction_panels import InteractionPanels
from ui.panels.create_actions import CreateActions
from ui.panels.runtime_actions import RuntimeActions
from ui.panels.object_factory import ObjectFactory
from ui.panels.animation_tools import AnimationTools
from ui.panels.property_helpers import PropertyHelpers
from ui.panels.app_actions import AppActions
from ui.panels.panel_delegates import PanelDelegates
from core.model_drag_drop import ModelDragDropService
from ssbo_component.ssbo_editor import SSBOEditor
from TransformGizmo.transform_gizmo import TransformGizmo

try:
    # 尝试导入视频管理器，避免循环导入
    import importlib.util
    spec = importlib.util.spec_from_file_location("video_integration", "demo/video_integration.py")
    video_module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(video_module)
    VideoManager = video_module.VideoManager
except:
    # 如果video_integration模块不存在，则跳过
    VideoManager = None
    print("⚠ 视频管理器模块未找到，视频功能将不可用")

warnings.filterwarnings("ignore", category=DeprecationWarning)

class MyWorld(PanelDelegates, CoreWorld):
    def __init__(self):
        super().__init__()
        
        # 设置窗口为最大化模式
        props = WindowProperties()
        #props.set_maximized(True)
        self.win.request_properties(props)
        print("✓ 窗口已设置为最大化模式")
        
        # 初始化选择和变换系统
        self.selection = SelectionSystem(self)

        # 绑定F键用于聚焦选中节点
        self.accept("f", self.onFocusKeyPressed)
        self.accept("F", self.onFocusKeyPressed)  # 大写F

        self.use_ssbo_mouse_picking = True
        self.use_ssbo_scene_import = True
        if not self.use_ssbo_mouse_picking:
            self.accept("mouse1", self.onMouseClick)
        # Keep release/move bindings even in SSBO mode so gizmo drag can work.
        self.accept("mouse1-up", self.onMouseRelease)
        self.accept("mouse-move", self.onMouseMove)
        self.accept("drag", self.onMouseMove)

        # 初始化事件处理系统
        self.event_handler = EventHandler(self)
        
        # 初始化工具管理系统
        self.tool_manager = ToolManager(self)
        
        # 初始化脚本管理系统
        self.script_manager = ScriptManager(self)

        # 初始化LUI管理系统
        self.lui_manager = LUIManager(self)

        # 新的坐标系
        self.newTransform = TransformGizmo(self)
        self._setup_transform_gizmo_light_sync()

        # 初始化视频管理
        if VideoManager is not None:
            self.video_manager = VideoManager(self)
        else:
            self.video_manager = None

        # 初始化场景管理系统
        # print(f"[DEBUG] 初始化场景管理系统...")
        self.scene_manager = SceneManager(self)
        # print(f"[DEBUG] 场景管理系统初始化完成")
        
        # 初始化项目管理系统
        # print(f"[DEBUG] 初始化项目管理系统...")
        self.project_manager = ProjectManager(self)
        # print(f"[DEBUG] 项目管理系统初始化完成")

        self.command_manager = CommandManager()

        # 初始化碰撞管理器
        self.collision_manager = CollisionManager(self)

        # 初始化资源管理器
        self.resource_manager = ResourceManager(self)

        # 初始化Actor缓存系统（用于动画控制）
        self._actor_cache = {}
        print("✓ Actor缓存系统初始化完成")

        # 初始化自定义鼠标控制器（视角移动）
        self.mouse_controller = CustomMouseController(self)
        self.mouse_controller.setUp(mouse_speed=25, move_speed=20)
        print("✓ 自定义鼠标控制器初始化完成")

        # 初始化VR管理器
        try:
            from core.vr import VRManager
            self.vr_manager = VRManager(self)
            print("✓ VR管理器初始化完成")
        except Exception as e:
            print(f"⚠ VR管理器初始化失败: {e}")
            self.vr_manager = None
        
        # VR调试相关变量
        self.vr_debug_enabled = False
        self.vr_detailed_mode = True
        self.vr_performance_monitor = False

        # 调试选项
        self.debug_collision = True  # 是否显示碰撞体

        # 默认启用模型间碰撞检测（可选）
        if self.use_ssbo_mouse_picking:
            self.enableModelCollisionDetection(enable=False, frequency=0.1, threshold=0.5)
        else:
            self.enableModelCollisionDetection(enable=True, frequency=0.1, threshold=0.5)
        
        # 碰撞检测UI相关变量
        self._selected_collision_shape = "球形 (Sphere)"  # 默认选择的碰撞形状

        # 启动脚本系统
        self.script_manager.start_system()

        self.terrain_manager = TerrainManager(self)

        self.terrain_edit_radius = 3.0
        self.terrain_edit_strength=0.3
        self.terrain_edit_operation = "add"

        # Install Dear ImGui
        p3dimgui.init(
            wantPlaceManager=False,
            wantExplorerManager=False,
            wantTimeSliderManager=False,
        )
        # Let ssbo_component reuse the existing imgui backend instance.
        self.imgui_backend = self.imgui
        # Initialize SSBO editor and let it own mouse1 picking.
        # Do not auto-load a default model here; models are loaded from import flow.
        self.ssbo_editor = None
        if self.use_ssbo_mouse_picking:
            self.ssbo_editor = SSBOEditor(
                base_app=self,
                render_pipeline=self.render_pipeline,
                model_path=None,
                font_path=None,
            )
            self.ssbo_editor.bind_transform_gizmo(self.newTransform)
            print("SSBOEditor mouse picking enabled (waiting for imported model)")

        imgui.get_io().config_flags |= imgui.ConfigFlags_.docking_enable
        print("✓ ImGui Docking功能已启用")

        # 初始化样式管理器
        from core.imgui_style_manager import ImGuiStyleManager
        self.style_manager = ImGuiStyleManager(self.imgui, self)
        self.editor_panels = EditorPanels(self)
        self.script_panels = ScriptPanels(self)
        self.dialog_panels = DialogPanels(self)
        self.interaction_panels = InteractionPanels(self)
        self.create_actions = CreateActions(self)
        self.runtime_actions = RuntimeActions(self)
        self.object_factory = ObjectFactory(self)
        self.animation_tools = AnimationTools(self)
        self.property_helpers = PropertyHelpers(self)
        self.app_actions = AppActions(self)
        
        # 简化的初始化字体设置（只使用中文字体）
        try:
            # 先清除默认字体
            self.imgui.io.fonts.clear()
            
            # 尝试加载中文字体
            import platform
            system = platform.system().lower()
            if system == "linux":
                font_path = "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"
            elif system == "windows":
                font_path = "C:/Windows/Fonts/msyh.ttc"
            elif system == "darwin":
                font_path = "/System/Library/Fonts/PingFang.ttc"
            else:
                font_path = None
            
            if font_path and Path(font_path).exists():
                self.imgui.io.fonts.add_font_from_file_ttf(font_path, 14.0)
                print(f"✓ 初始化中文字体成功: {font_path}")
            else:
                # 回退到原来的字体
                fallback_font_path = "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc"
                if fallback_font_path and Path(fallback_font_path).exists():
                    self.imgui.io.fonts.add_font_from_file_ttf(fallback_font_path, 14.0)
                    print(f"✓ 初始化回退字体成功: {fallback_font_path}")
                else:
                    self.imgui.io.fonts.add_font_default()
                    print("✓ 初始化使用默认字体")
                
        except Exception as e:
            print(f"⚠ 初始化字体系统失败: {e}")
            # 备用方案：使用默认字体
            try:
                self.imgui.io.fonts.add_font_default()
                print("✓ 使用备用默认字体")
            except:
                pass

        # Disable the camera trackball controls.
        self.disableMouse()

        self.showDemoWindow = False

        # UI状态管理
        self.showSceneTree = True
        self.showPropertyPanel = True
        self.showConsole = True
        self.showScriptPanel = not self.use_ssbo_mouse_picking
        self.showToolbar = True
        self.showResourceManager = True
        self.showWebPanel = False
        self.webPanelUrl = "https://www.baidu.com"
        
        # 脚本系统状态变量
        self.hotReloadEnabled = True
        self._new_script_name = "new_script"
        self._selected_template = 0
        self._mount_script_index = 0
        
        # 变换监控相关
        self._transform_monitoring = False
        self._monitored_node = None
        self._last_transform_values = {}
        self._transform_update_timer = 0
        self._transform_update_interval = 0.05  # 50ms检查一次
        self._clipboard_pos = None  # 位置剪贴板
        
        # 颜色选择器相关
        self._color_picker_active = False
        self._color_picker_target = None  # (target_object, property_name)
        self._color_picker_current_color = (1.0, 1.0, 1.0, 1.0)
        self._color_picker_callback = None
        
        # 字体选择器相关
        self._font_selector_active = False
        self._font_selector_target = None  # (target_object, property_name)
        self._font_selector_current_font = ""
        self._font_selector_callback = None
        self._available_fonts = []  # 可用字体列表
        self._refresh_available_fonts()

        # 菜单状态管理
        self.show_new_project_dialog = False
        self.show_open_project_dialog = False
        self.show_save_as_dialog = False
        
        # 路径选择对话框状态
        self.show_path_browser = False
        self.path_browser_mode = ""  # "new_project" 或 "open_project"
        self.path_browser_current_path = os.getcwd()
        self.path_browser_selected_path = ""
        self.path_browser_items = []
        
        # 快捷键映射
        self.shortcut_keys = {
            'Ctrl+N': self._on_new_project,
            'Ctrl+O': self._on_open_project,
            'Ctrl+S': self._on_save_project,
            'Alt+F4': self._on_exit
        }
        
        # 键盘状态跟踪
        self.ctrl_pressed = False
        self.alt_pressed = False
        
        # 消息系统
        self.messages = []
        self.max_messages = 5  # 最多显示5条消息
        
        # 剪切板系统
        self.clipboard = []
        self.clipboard_mode = ""  # "copy" 或 "cut"
        
        # 视角控制状态
        self.camera_control_enabled = True
        self.show_camera_info = False
        
        # 拖拽导入状态
        self.dragged_files = []
        self.is_dragging = False
        self.show_drag_overlay = False
        self.drag_drop_monitor = None
        self._resource_manager_window_rect = None
        self._resource_drop_targets = []
        self._scene_tree_window_rect = None
        self._property_panel_window_rect = None
        self._script_panel_window_rect = None
        self._console_window_rect = None
        self._toolbar_window_rect = None
        self._drag_scene_tree_hover_node = None
        self.model_drag_drop = ModelDragDropService(self)
        self.showLUIEditor = not self.use_ssbo_mouse_picking
        
        # 导入功能状态
        self.show_import_dialog = False
        self.import_file_path = ""
        self.supported_formats = [".gltf", ".glb", ".fbx", ".bam", ".egg", ".obj"]
        
        # 创建功能状态
        self.show_3d_text_dialog = False
        self.show_3d_image_dialog = False
        self.show_gui_button_dialog = False
        self.show_gui_label_dialog = False
        self.show_gui_entry_dialog = False
        self.show_gui_image_dialog = False
        self.show_video_screen_dialog = False
        self.show_2d_video_screen_dialog = False
        self.show_spherical_video_dialog = False
        self.show_virtual_screen_dialog = False
        self.show_spot_light_dialog = False
        self.show_point_light_dialog = False
        self.show_terrain_dialog = False
        self.show_heightmap_browser = False
        self.show_script_dialog = False
        self.show_script_browser = False
        
        # 高度图浏览器状态
        self.heightmap_browser_current_path = os.getcwd()
        self.heightmap_browser_selected_path = ""
        self.heightmap_browser_items = []
        self.heightmap_file_path = ""
        self.supported_heightmap_formats = [".png", ".jpg", ".jpeg", ".bmp", ".tiff", ".tif"]
        
        # 脚本浏览器状态
        self.script_browser_current_path = os.getcwd()
        self.script_browser_selected_path = ""
        self.script_browser_items = []
        
        # 对话框参数存储
        self.dialog_params = {}  # 存储各种对话框的参数
        
        # 脚本系统状态
        self.hotReloadEnabled = False
        
        # 初始化高度图浏览器
        self._refresh_heightmap_browser()
        
        # 初始化脚本浏览器
        self._refresh_script_browser()

        self.accept('imgui-new-frame', self.__newFrame)
        self.accept('`', self.__toggleImgui)
        
        # 添加键盘事件监听用于快捷键
        self.accept('control', self._on_ctrl_pressed)
        self.accept('control-up', self._on_ctrl_released)
        self.accept('alt', self._on_alt_pressed)
        self.accept('alt-up', self._on_alt_released)
        self.accept('n', self._on_n_pressed)
        self.accept('o', self._on_o_pressed)
        self.accept('control-s', self._on_save_project)
        self.accept('f4', self._on_f4_pressed)
        
        # 编辑功能快捷键
        self.accept('control-z', self._on_undo)
        self.accept('control-y', self._on_redo)
        self.accept('control-x', self._on_cut)
        self.accept('control-c', self._on_copy)
        self.accept('control-v', self._on_paste)
        self.accept('delete', self._on_delete_pressed)
        self.accept('escape', self._on_escape_pressed)
        
        # 滚轮事件
        self.accept('wheel_up', self._on_wheel_up)
        self.accept('wheel_down', self._on_wheel_down)

        self.testTexture = None
        self.icons = {}  # 初始化图标字典

        # 添加初始化消息
        self.add_success_message("MyWorld 初始化完成")
        self.add_info_message("ImGui菜单系统已就绪")
        self.add_info_message("快捷键已启用 (Ctrl+N, Ctrl+O, Ctrl+S, Alt+F4)")
        
        # 启用拖拽导入功能
        self.setup_drag_drop_support()
        self.add_info_message("拖拽导入功能已启用 - 可将3D文件拖拽到窗口中导入")

        print("✓ MyWorld 初始化完成")

    # ==================== 兼容性属性 ====================

    # 保留models属性以兼容现有代码
    @property
    def models(self):
        """模型列表的兼容性属性"""
        return self.scene_manager.models

    @models.setter
    def models(self, value):
        """模型列表的兼容性设置器"""
        self.scene_manager.models = value


    # 保留currentTool属性以兼容现有代码
    @property
    def currentTool(self):
        """当前工具的兼容性属性"""
        return self.tool_manager.currentTool

    @currentTool.setter
    def currentTool(self, value):
        """当前工具的兼容性设置器"""
        self.tool_manager.currentTool = value

    # 保留terrains属性以兼容现有代码
    @property
    def terrains(self):
        """地形列表的兼容性属性"""
        return self.terrain_manager.terrains

    @terrains.setter
    def terrains(self,value):
        """地形列表的兼容性设置器"""
        self.terrain_manager.terrains = value

    def onFocusKeyPressed(self):
        """处理 F 键按下事件"""
        try:
            if hasattr(self, 'selection') and self.selection.selectedNode:
                self.selection.focusCameraOnSelectedNodeAdvanced()
            else:
                print("当前没有选中任何节点")
        except Exception as e:
            print(f"处理 F 键事件失败: {e}")

    def onMouseClick(self):
        """处理鼠标点击事件"""
        print("\n=== 鼠标点击事件触发 ===")
        try:
            # 检查鼠标是否有效
            if not self.mouseWatcherNode.hasMouse():
                print("❌ 鼠标无效或不在窗口内")
                return
            
            print("✓ 鼠标位置有效")
            
            # 获取鼠标位置
            mouse_x = self.mouseWatcherNode.getMouseX()
            mouse_y = self.mouseWatcherNode.getMouseY()
            print(f"📍 鼠标标准化坐标: ({mouse_x:.3f}, {mouse_y:.3f})")
            
            # 转换为窗口坐标
            winWidth, winHeight = self.win.getSize()
            window_x = (mouse_x + 1) * 0.5 * winWidth
            window_y = (1 - mouse_y) * 0.5 * winHeight
            print(f"📍 鼠标窗口坐标: ({window_x:.1f}, {window_y:.1f})")
            print(f"📐 窗口尺寸: {winWidth} x {winHeight}")
            
            # 检查ImGui是否捕获了鼠标
            imgui_captured = self.processImGuiMouseClick(window_x, window_y)
            print(f"🖱️ ImGui捕获状态: {imgui_captured}")
            if imgui_captured:
                print("❌ ImGui处理了该事件，跳过3D场景选择")
                return
            
            # 调用事件处理器进行射线检测和选择
            if hasattr(self, 'event_handler'):
                print("✓ 找到事件处理器，开始处理选择")
                self.event_handler.mousePressEventLeft({
                    'x': window_x,
                    'y': window_y
                })
            else:
                print("❌ 未找到事件处理器")
            
        except Exception as e:
            print(f"❌ 处理鼠标点击事件失败: {e}")
            import traceback
            traceback.print_exc()

    def onMouseRelease(self):
        """处理鼠标释放事件"""
        try:
            # 检查鼠标是否有效
            if not self.mouseWatcherNode.hasMouse():
                return
            
            # 获取鼠标位置
            mouse_x = self.mouseWatcherNode.getMouseX()
            mouse_y = self.mouseWatcherNode.getMouseY()
            
            # 转换为窗口坐标
            winWidth, winHeight = self.win.getSize()
            window_x = (mouse_x + 1) * 0.5 * winWidth
            window_y = (1 - mouse_y) * 0.5 * winHeight
            
            # 调用事件处理器
            if hasattr(self, 'event_handler'):
                self.event_handler.mouseReleaseEventLeft({
                    'x': window_x,
                    'y': window_y
                })
            
        except Exception as e:
            print(f"处理鼠标释放事件失败: {e}")

    def onMouseMove(self):
        """处理鼠标移动事件"""
        try:
            # 检查鼠标是否有效
            if not self.mouseWatcherNode.hasMouse():
                return
            
            # 获取鼠标位置
            mouse_x = self.mouseWatcherNode.getMouseX()
            mouse_y = self.mouseWatcherNode.getMouseY()
            
            # 转换为窗口坐标
            winWidth, winHeight = self.win.getSize()
            window_x = (mouse_x + 1) * 0.5 * winWidth
            window_y = (1 - mouse_y) * 0.5 * winHeight
            
            # 调用事件处理器
            if hasattr(self, 'event_handler'):
                self.event_handler.mouseMoveEvent({
                    'x': window_x,
                    'y': window_y
                })
            
        except Exception as e:
            print(f"处理鼠标移动事件失败: {e}")


    def enableModelCollisionDetection(self, enable=True, frequency=0.1, threshold=0.5):
        """启用模型间碰撞检测"""
        return self.collision_manager.enableModelCollisionDetection(enable, frequency, threshold)

    def __toggleImgui(self):
        if not self.imgui.isKeyboardCaptured():
            self.imgui.toggle()

    def __newFrame(self):
        # Dear ImGui commands can be placed here.
        
        # 创建全屏DockSpace（在第一帧之后创建）
        if imgui.get_frame_count() > 0:
            viewport = imgui.get_main_viewport()
            imgui.dock_space_over_viewport(0, viewport, imgui.DockNodeFlags_.passthru_central_node)
        
        # 在第一帧应用样式
        if imgui.get_frame_count() == 0:
            self.style_manager.apply_style()
            # 加载图标
            self.icons = {
                'select': self.style_manager.load_icon('select_tool'),
                'move': self.style_manager.load_icon('move_tool'),
                'rotate': self.style_manager.load_icon('rotate_tool'),
                'scale': self.style_manager.load_icon('scale_tool'),
                'success': self.style_manager.load_icon('success_icon'),
                'warning': self.style_manager.load_icon('warning_icon'),
                'property_select_image': self.style_manager.load_icon('property_select_image'),
                'delete_fail_icon': self.style_manager.load_icon('delete_fail_icon'),
            }
            
            # 简化字体加载（只使用中文字体）
            try:
                # 清除现有字体
                self.imgui.io.fonts.clear()
                
                # 尝试加载中文字体
                import platform
                from pathlib import Path
                
                system = platform.system().lower()
                if system == "linux":
                    font_path = "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"
                elif system == "windows":
                    font_path = "C:/Windows/Fonts/msyh.ttc"
                elif system == "darwin":
                    font_path = "/System/Library/Fonts/PingFang.ttc"
                else:
                    font_path = None
                
                if font_path and Path(font_path).exists():
                    self.imgui.io.fonts.add_font_from_file_ttf(font_path, 14.0)
                    print(f"✓ 中文字体加载成功: {font_path}")
                else:
                    # 回退到原来的字体
                    fallback_font_path = "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc"
                    if fallback_font_path and Path(fallback_font_path).exists():
                        self.imgui.io.fonts.add_font_from_file_ttf(fallback_font_path, 14.0)
                        print(f"✓ 回退字体加载成功: {fallback_font_path}")
                    else:
                        self.imgui.io.fonts.add_font_default()
                        print("✓ 使用默认字体")
                    
            except Exception as e:
                print(f"⚠ 字体初始化失败: {e}")
                try:
                    self.imgui.io.fonts.add_font_default()
                    print("✓ 使用备用默认字体")
                except:
                    pass
        
        # 获取窗口尺寸
        display_size = imgui.get_io().display_size
        window_width = display_size.x
        window_height = display_size.y

        # 每帧重置窗口命中区域，由各面板绘制时重新填充
        self._resource_manager_window_rect = None
        self._resource_drop_targets = []
        self._scene_tree_window_rect = None
        self._property_panel_window_rect = None
        self._script_panel_window_rect = None
        self._console_window_rect = None
        self._toolbar_window_rect = None
        self._drag_scene_tree_hover_node = None
        
        # 绘制菜单栏
        self._draw_menu_bar()
        
        # 绘制停靠布局
        self._draw_docked_layout(window_width, window_height)

        # 处理系统层外部拖入
        self._process_external_drop_events()
        
        # 绘制纹理测试窗口
        if self.testTexture:
            with self.style_manager.begin_styled_window("Texture Test", True, 
                                                      imgui.WindowFlags_.always_auto_resize) as (_, windowOpen):
                if not windowOpen:
                    self.imgui.removeTexture(self.testTexture)
                    self.testTexture = None
                    return
                imgui.image(self.testTexture, (256, 256))
        
        # 绘制ImGui演示窗口
        if self.showDemoWindow:
            imgui.show_demo_window()
        
        # 绘制对话框
        self._draw_new_project_dialog()
        self._draw_open_project_dialog()
        self._draw_path_browser()
        self._draw_import_dialog()
        
        # 绘制颜色选择器
        self._draw_color_picker()
        
        # 绘制字体选择器
        self._draw_font_selector()
        
        self._draw_spot_light_dialog()
        self._draw_point_light_dialog()
        self._draw_terrain_dialog()
        self._draw_heightmap_browser()
        self._draw_script_dialog()
        
        # 绘制纹理选择对话框
        self._draw_texture_file_dialog()
        self._draw_script_browser()
        
        # 绘制右键菜单
        self._draw_context_menus()
        
        # 绘制拖拽界面
        self._draw_drag_drop_interface()
        
        # 绘制LUI编辑器
        if self.showLUIEditor and hasattr(self, 'lui_manager'):
            self.lui_manager.draw_editor()
        
        # 更新变换监控
        dt = imgui.get_io().delta_time
        self.update_transform_monitoring(dt)
    
    def _draw_docked_layout(self, window_width, window_height):
        """绘制可停靠的布局（支持拖拽）"""
        # 左侧场景树面板
        if self.showSceneTree:
            self._draw_scene_tree()
        
        # 资源管理器面板
        if self.showResourceManager:
            self._draw_resource_manager()
        
        # 属性面板
        if self.showPropertyPanel:
            self._draw_property_panel()
        
        # Web面板
        if self.showWebPanel:
            self._draw_web_panel()
        
        # 脚本面板
        if self.showScriptPanel:
            self._draw_script_panel()
        
        # 底部控制台
        if self.showConsole:
            self._draw_console()
        
        # 顶部工具栏
        if self.showToolbar:
            self._draw_toolbar()

    def _sync_rp_light_from_node(self, node):
        """将灯光包装节点的位置同步到 RenderPipeline 灯光对象。"""
        try:
            if not node or node.isEmpty() or (not node.hasPythonTag("rp_light_object")):
                # 兼容旧场景：仅有 light_type 标签时尝试补绑 rp_light_object
                if not node or node.isEmpty() or (not node.hasTag("light_type")):
                    return False
                scene_manager = getattr(self, "scene_manager", None)
                if scene_manager:
                    try:
                        light_type = node.getTag("light_type")
                        if light_type == "spot_light" and hasattr(scene_manager, "_recreateSpotLight"):
                            scene_manager._recreateSpotLight(node)
                        elif light_type == "point_light" and hasattr(scene_manager, "_recreatePointLight"):
                            scene_manager._recreatePointLight(node)
                    except Exception:
                        pass
            light_obj = node.getPythonTag("rp_light_object") if node.hasPythonTag("rp_light_object") else None
            if not light_obj:
                return False

            world_pos = node.getPos(self.render)
            try:
                light_obj.setPos(world_pos)
            except Exception:
                try:
                    light_obj.setPos(world_pos.x, world_pos.y, world_pos.z)
                except Exception:
                    try:
                        light_obj.pos = Point3(world_pos)
                    except Exception:
                        return False
            return True
        except Exception:
            return False

    def _on_transform_gizmo_drag_event(self, payload):
        """TransformGizmo 拖拽事件回调：实时同步灯光位置。"""
        try:
            node = payload.get("target") if isinstance(payload, dict) else None
            if node and (not node.isEmpty()):
                self._sync_rp_light_from_node(node)
        except Exception:
            pass

    def _setup_transform_gizmo_light_sync(self):
        """为 newTransform 注册灯光同步事件钩子。"""
        tg = getattr(self, "newTransform", None)
        if not tg:
            return
        try:
            from TransformGizmo.events import GizmoEvent
            hooks = {
                "move": {
                    GizmoEvent.DRAG_MOVE: [self._on_transform_gizmo_drag_event],
                    GizmoEvent.DRAG_END: [self._on_transform_gizmo_drag_event],
                },
                "rotate": {
                    GizmoEvent.DRAG_MOVE: [self._on_transform_gizmo_drag_event],
                    GizmoEvent.DRAG_END: [self._on_transform_gizmo_drag_event],
                },
                "scale": {
                    GizmoEvent.DRAG_MOVE: [self._on_transform_gizmo_drag_event],
                    GizmoEvent.DRAG_END: [self._on_transform_gizmo_drag_event],
                },
            }
            tg.set_event_hooks(hooks, replace=False)
        except Exception as e:
            print(f"绑定 TransformGizmo 灯光同步事件失败: {e}")
    
if __name__ == "__main__":
    demo = MyWorld()
    demo.run()
