from panda3d.core import (Point3, Point2, CollisionTraverser, CollisionHandlerQueue,
                         CollisionNode, CollisionRay, GeomNode, LineSegs, RenderState,
                         DepthTestAttrib, ColorAttrib)


class EventHandler:
    """事件处理器 - 处理鼠标和键盘输入事件"""
    
    def __init__(self, world):
        """初始化事件处理器"""
        self.world = world
        
        # 射线显示相关
        self.showRay = False  # 是否显示射线（默认关闭）
        self.rayNode = None  # 当前显示的射线节点
        self.rayLifetime = 2.0  # 射线显示时间（秒）
    
    def showClickRay(self, nearPoint, farPoint, hitPos=None):
        """显示鼠标点击的射线"""
        if not self.showRay:
            return
            
        try:
            # 清除之前的射线
            self.clearRay()
            
            # 创建射线几何体
            lines = LineSegs()
            lines.setThickness(3.0)
            
            # 设置射线颜色
            if hitPos:
                # 有碰撞：射线分两段，起点到碰撞点为绿色，碰撞点到终点为红色
                lines.setColor(0, 1, 0, 1)  # 绿色
                lines.moveTo(nearPoint)
                lines.drawTo(hitPos)
                
                lines.setColor(1, 0, 0, 1)  # 红色
                lines.moveTo(hitPos)
                lines.drawTo(farPoint)
                
                # 在碰撞点添加一个小球
                lines.setColor(1, 1, 0, 1)  # 黄色
                self._addHitMarker(lines, hitPos)
            else:
                # 无碰撞：整条射线为蓝色
                lines.setColor(0, 0, 1, 1)  # 蓝色
                lines.moveTo(nearPoint)
                lines.drawTo(farPoint)
            
            # 创建射线节点
            geomNode = lines.create()
            self.rayNode = self.world.render.attachNewNode(geomNode)
            self.rayNode.setName("clickRay")
            
            # 设置渲染状态，确保射线总是可见
            state = RenderState.make(
                DepthTestAttrib.make(DepthTestAttrib.MAlways),  # 总是通过深度测试
                ColorAttrib.makeFlat((1.0, 1.0, 1.0, 1.0))
            )
            self.rayNode.setState(state)
            self.rayNode.setLightOff()  # 不受光照影响
            
            # 设置自动清除任务（先清除可能存在的旧任务）
            from direct.task.TaskManagerGlobal import taskMgr
            taskMgr.remove("clearRay")  # 清除可能存在的旧任务
            taskMgr.doMethodLater(self.rayLifetime, self.clearRayTask, "clearRay")
            
            print(f"✓ 射线已显示，{self.rayLifetime}秒后自动清除")
            
        except Exception as e:
            print(f"显示射线失败: {str(e)}")
    
    def _addHitMarker(self, lines, hitPos):
        """在碰撞点添加标记"""
        # 创建一个小十字标记
        marker_size = 0.5
        
        # X方向线
        lines.moveTo(hitPos.x - marker_size, hitPos.y, hitPos.z)
        lines.drawTo(hitPos.x + marker_size, hitPos.y, hitPos.z)
        
        # Y方向线
        lines.moveTo(hitPos.x, hitPos.y - marker_size, hitPos.z)
        lines.drawTo(hitPos.x, hitPos.y + marker_size, hitPos.z)
        
        # Z方向线
        lines.moveTo(hitPos.x, hitPos.y, hitPos.z - marker_size)
        lines.drawTo(hitPos.x, hitPos.y, hitPos.z + marker_size)
    
    def clearRay(self):
        """清除当前显示的射线"""
        if self.rayNode:
            self.rayNode.removeNode()
            self.rayNode = None
        
        # 同时清除可能存在的任务
        from direct.task.TaskManagerGlobal import taskMgr
        taskMgr.remove("clearRay")
    
    def clearRayTask(self, task):
        """清除射线的任务回调"""
        self.clearRay()
        return task.done
    
    def toggleRayDisplay(self):
        """切换射线显示状态"""
        self.showRay = not self.showRay
        if not self.showRay:
            self.clearRay()
        print(f"射线显示: {'开启' if self.showRay else '关闭'}")
        return self.showRay
    
    def mousePressEventLeft(self, evt):
        """处理鼠标左键按下事件"""
        print("\n=== EventHandler开始处理鼠标左键事件 ===")
        print(f"🔧 当前工具: {getattr(self.world, 'currentTool', '未知')}")
        
        if not evt:
            print("❌ 事件为空")
            return
        if self.world.currentTool == "地形编辑":
            print("🔧 地形编辑模式，调用地形编辑处理")
            self._handleTerrainEdit(evt,"add")
            return

        # 获取鼠标点击的位置
        x = evt.get('x', 0)
        y = evt.get('y', 0)
        print(f"📍 EventHandler收到鼠标点击位置: ({x:.1f}, {y:.1f})")
        
        # 获取准确的窗口尺寸
        winWidth, winHeight = self.world.getWindowSize()
        
        # 直接使用 x, y 创建鼠标位置
        mx = 2.0 * x / float(winWidth) - 1.0
        my = 1.0 - 2.0 * y / float(winHeight)
        #print(f"转换后的坐标: ({mx}, {my})")
        
        # 创建射线
        nearPoint = Point3()
        farPoint = Point3()
        self.world.cam.node().getLens().extrude(Point2(mx, my), nearPoint, farPoint)
        #print(f"相机坐标系射线起点: {nearPoint}")
        #print(f"相机坐标系射线终点: {farPoint}")
        
        # 将相机坐标系的点转换到世界坐标系
        worldNearPoint = self.world.render.getRelativePoint(self.world.cam, nearPoint)
        worldFarPoint = self.world.render.getRelativePoint(self.world.cam, farPoint)
        #print(f"世界坐标系射线起点: {worldNearPoint}")
        #print(f"世界坐标系射线终点: {worldFarPoint}")
        
        # 进行射线检测
        picker = CollisionTraverser()
        queue = CollisionHandlerQueue()
        
        pickerNode = CollisionNode('mouseRay')
        pickerNP = self.world.cam.attachNewNode(pickerNode)
        # 设置射线的碰撞掩码，匹配模型的碰撞掩码（第2位）
        from panda3d.core import BitMask32
        pickerNode.setFromCollideMask(BitMask32.bit(2))
        
        # 使用相机坐标系的点创建射线（因为pickerNP是相机的子节点）
        direction = farPoint - nearPoint
        direction.normalize()
        pickerNode.addSolid(CollisionRay(nearPoint, direction))
        
        picker.addCollider(pickerNP, queue)
        picker.traverse(self.world.render)
        
        #print(f"碰撞检测结果数量: {queue.getNumEntries()}")
        
        # 射线检测结果处理
        hitPos = None
        hitNode = None
        
        if queue.getNumEntries() > 0:
            # 获取最近的碰撞点
            entry = queue.getEntry(0)
            hitPos = entry.getSurfacePoint(self.world.render)
            hitNode = entry.getIntoNodePath()
            print(f"碰撞到节点: {hitNode.getName()}")
        
        # 显示射线（使用世界坐标系的点）
        self.showClickRay(worldNearPoint, worldFarPoint, hitPos)
        
        # 处理ImGui鼠标点击
        if hasattr(self.world, 'processImGuiMouseClick'):
            if self.world.processImGuiMouseClick(x, y):
                # 如果ImGui处理了点击，不再进行其他处理
                pickerNP.removeNode()
                return
        
        # 优先检查是否点击了坐标轴
        #print(f"检查坐标轴点击: 坐标轴存在={bool(self.world.selection.gizmo)}")
        if self.world.selection.gizmo:
            #print("准备检查坐标轴点击...")
            try:
                highlighted_axis = self.world.selection.gizmoHighlightAxis
                if highlighted_axis:
                    print(f"✓ 检测到高亮轴: {highlighted_axis}，直接开始拖拽")
                    # 直接使用高亮轴开始拖拽
                    self.world.selection.startGizmoDrag(highlighted_axis, x, y)
                    pickerNP.removeNode()
                    return

                # 如果没有高亮轴，再尝试检测点击
                gizmoAxis = self.world.selection.checkGizmoClick(x, y)
                if gizmoAxis:
                    print(f"✓ 检测到坐标轴点击: {gizmoAxis}")
                    # 开始坐标轴拖拽
                    self.world.selection.startGizmoDrag(gizmoAxis, x, y)
                    pickerNP.removeNode()
                    return
                else:
                    print("× 没有点击到坐标轴")

                # gizmoAxis = self.world.selection.checkGizmoClick(x, y)
                # if gizmoAxis:
                #     #print(f"✓ 检测到坐标轴点击: {gizmoAxis}")
                #     # 开始坐标轴拖拽
                #     self.world.selection.startGizmoDrag(gizmoAxis, x, y)
                #     pickerNP.removeNode()
                #     return
                # else:
                #     print("× 没有点击到坐标轴")
            except Exception as e:
                print(f"❌ 坐标轴点击检测出现异常: {str(e)}")
                import traceback
                traceback.print_exc()
                print("继续处理模型选择...")
        
        #print("继续处理碰撞结果...")
        
        if hitPos and hitNode:
            #print(f"✓ 检测到碰撞，开始处理点击事件")
            #print(f"GUI编辑模式: {self.world.guiEditMode}")
            #print(f"当前工具: {self.world.currentTool}")
            
            # 处理GUI编辑模式
            if self.world.guiEditMode:
                #print("处理GUI编辑模式点击")
                # 检查是否点击了GUI元素
                clickedGUI = self.world.gui_manager.findClickedGUI(hitNode)
                if clickedGUI:
                    # 选中GUI元素
                    self.world.selection.updateSelection(clickedGUI)
                    self.world.gui_manager.selectGUIInTree(clickedGUI)
                    print(f"选中GUI元素: {clickedGUI.getTag('gui_text')}")
                elif hasattr(self.world, 'currentGUITool') and self.world.currentGUITool:
                    # 在点击位置创建新GUI元素
                    self.world.gui_manager.createGUIAtPosition(hitPos, self.world.currentGUITool)
                pickerNP.removeNode()
                return
            
            # 根据当前工具处理点击事件
            if self.world.currentTool == "选择":
                print("✓ 使用选择工具处理点击")
                try:
                    self._handleSelectionClick(hitNode)
                    print("✓ 选择处理完成")
                except Exception as e:
                    print(f"❌ 选择处理出现异常: {str(e)}")
                    import traceback
                    traceback.print_exc()
            else:
                print(f"当前工具不是'选择'，无法处理: {self.world.currentTool}")
        else:
            print("没有检测到碰撞")
            
            # 如果不在GUI编辑模式，清除选择
            if not self.world.guiEditMode:
                self.world.selection.updateSelection(None)
            
            # 在GUI编辑模式下，即使没有碰撞也可以在空白区域创建GUI
            if (self.world.guiEditMode and 
                hasattr(self.world, 'currentGUITool') and 
                self.world.currentGUITool):
                # 使用默认的地面高度创建GUI
                default_height = 0.0
                world_pos = Point3(mx * 10, 0, my * 10)  # 简单的屏幕到世界坐标转换
                world_pos.setZ(default_height)
                self.world.gui_manager.createGUIAtPosition(world_pos, self.world.currentGUITool)
        
        # 确保总是清理碰撞检测节点
        try:
            pickerNP.removeNode()
            print("✓ 碰撞检测节点已清理")
        except Exception as e:
            print(f"清理碰撞检测节点失败: {str(e)}")
            
        print("=== 鼠标左键事件处理结束 ===\n")

    def mousePressEventRight(self,evt):
        """处理鼠标右键按下事件"""
        print(f"当前工具: {self.world.currentTool}")

        # 检查是否是地形编辑模式
        if self.world.currentTool == "地形编辑":
            self._handleTerrainEdit(evt, "subtract")  # 降低地形
            return

        # 其他右键处理逻辑可以在这里添加
        print("鼠标右键事件处理")

    def _handleTerrainEdit(self, evt, operation):
        """处理地形编辑"""
        try:
            x = evt.get('x', 0)
            y = evt.get('y', 0)

            winWidth, winHeight = self.world.getWindowSize()

            mx = 2.0 * x / float(winWidth) - 1.0
            my = 1.0 - 2.0 * y / float(winHeight)

            nearPoint = Point3()
            farPoint = Point3()
            self.world.cam.node().getLens().extrude(Point2(mx, my), nearPoint, farPoint)

            worldNearPoint = self.world.render.getRelativePoint(self.world.cam, nearPoint)
            worldFarPoint = self.world.render.getRelativePoint(self.world.cam, farPoint)

            picker = CollisionTraverser()
            queue = CollisionHandlerQueue()

            pickerNode = CollisionNode('terrain_edit_ray')
            pickerNP = self.world.cam.attachNewNode(pickerNode)

            from panda3d.core import BitMask32
            # 确保我们检测所有碰撞体
            pickerNode.setFromCollideMask(BitMask32.bit(2))

            # 使用相机坐标系的点创建射线
            direction = farPoint - nearPoint
            direction.normalize()
            pickerNode.addSolid(CollisionRay(nearPoint, direction))

            picker.addCollider(pickerNP, queue)
            picker.traverse(self.world.render)
            print(f"地形碰撞检测结果数量: {queue.getNumEntries()}")

            # 添加调试信息，显示所有场景中的碰撞体
            print("场景中的碰撞体:")

            def show_colliders(node, level=0):
                indent = "  " * level
                if not node.isEmpty():
                    if isinstance(node.node(), CollisionNode):
                        print(f"{indent}CollisionNode: {node.getName()}")
                    for child in node.getChildren():
                        show_colliders(child, level + 1)

            show_colliders(self.world.render)

            # 射线检测结果处理
            hitPos = None
            hitNode = None
            hitTerrainInfo = None

            if queue.getNumEntries() > 0:
                queue.sortEntries()
                # 遍历所有碰撞结果，找到地形节点
                for i in range(queue.getNumEntries()):
                    entry = queue.getEntry(i)
                    collided_node = entry.getIntoNodePath()
                    print(f"碰撞到节点: {collided_node.getName()}")

                    # 检查是否是地形节点
                    for terrain_info in self.world.terrain_manager.terrains:
                        terrain_node = terrain_info['node']
                        if collided_node == terrain_node or terrain_node.isAncestorOf(collided_node):
                            hitPos = entry.getSurfacePoint(self.world.render)
                            hitNode = collided_node
                            hitTerrainInfo = terrain_info
                            print(f"找到地形节点: {terrain_node.getName()}")
                            break

                    if hitPos:
                        break

            if hitPos and hitTerrainInfo:
                # 修改地形高度
                x_pos, y_pos = hitPos.getX(), hitPos.getY()

                # 使用 getattr 获取地形编辑参数
                radius = getattr(self.world, 'terrain_edit_radius', 3.0)
                strength = getattr(self.world, 'terrain_edit_strength', 0.3)

                print(f"准备编辑地形: 位置({x_pos:.2f}, {y_pos:.2f}), 半径{radius}, 强度{strength}, 操作{operation}")

                # 添加更多调试信息
                print(f"地形信息: {hitTerrainInfo}")
                if 'heightmap' in hitTerrainInfo:
                    print(f"高度图路径: {hitTerrainInfo['heightmap']}")

                try:
                    # 检查 terrain_manager 是否有 modifyTerrainHeight 方法
                    if not hasattr(self.world.terrain_manager, 'modifyTerrainHeight'):
                        print("✗ 错误: terrain_manager 没有 modifyTerrainHeight 方法")
                        self.showClickRay(worldNearPoint, worldFarPoint, hitPos)
                        return

                    # 调用地形编辑方法
                    success = self.world.terrain_manager.modifyTerrainHeight(
                        hitTerrainInfo, x_pos, y_pos, radius=radius, strength=strength, operation=operation)

                    if success:
                        print(f"✓ 地形编辑成功: {operation} at ({x_pos:.2f}, {y_pos:.2f})")
                        # 显示射线
                        self.showClickRay(worldNearPoint, worldFarPoint, hitPos)

                        # 强制刷新地形显示
                        if 'terrain' in hitTerrainInfo:
                            hitTerrainInfo['terrain'].generate()
                            print("✓ 地形已刷新")
                    else:
                        print("✗ 地形编辑失败")
                        # 即使编辑失败也显示射线
                        self.showClickRay(worldNearPoint, worldFarPoint, hitPos)
                except Exception as e:
                    print(f"✗ 地形编辑过程中出现异常: {e}")
                    import traceback
                    traceback.print_exc()
                    # 即使编辑失败也显示射线
                    self.showClickRay(worldNearPoint, worldFarPoint, hitPos)
            else:
                print("没有检测到地形碰撞")
                # 显示射线（无碰撞）
                self.showClickRay(worldNearPoint, worldFarPoint)

            # 清理碰撞检测节点
            pickerNP.removeNode()
            print("地形编辑处理完成")

        except Exception as e:
            print(f"地形编辑处理出错: {e}")
            import traceback
            traceback.print_exc()

    def _handleSelectionClick(self, hitNode):
        """处理选择工具的点击事件"""
        print(f"开始处理选择点击，碰撞节点: {hitNode.getName()}")
        
        # 查找对应的实际模型节点
        selectedModel = None
        
        # 如果点击的是碰撞节点，找到它的父模型
        if isinstance(hitNode.node(), CollisionNode):
            print(f"点击的是碰撞节点: {hitNode.getName()}")
            # 碰撞节点的父节点应该是模型
            parent = hitNode.getParent()
            if parent in self.world.models:
                selectedModel = parent
                print(f"找到对应的模型: {selectedModel.getName()}")
            else:
                print(f"碰撞节点的父节点不是模型: {parent.getName()}")
        else:
            # 查找可选择的节点（模型或其子节点）
            current = hitNode
            while current != self.world.render:
                # 检查是否是模型
                if current in self.world.models:
                    selectedModel = current
                    print(f"找到模型节点: {selectedModel.getName()}")
                    break
                    
                # 检查是否是模型的子节点
                for model in self.world.models:
                    if current.getParent() == model or current.isAncestorOf(model):
                        selectedModel = model
                        print(f"找到父模型: {selectedModel.getName()}")
                        break

                if selectedModel:
                    break
                    
                current = current.getParent()
        
        if selectedModel:
            #print(f"✓ 最终选中模型: {selectedModel.getName()}")
            self.world.selection.handleMouseClick(selectedModel)
            
            # 更新选择状态并显示选择框和坐标轴
            self.world.selection.updateSelection(selectedModel)
            
            # 在树形控件中查找并选中对应的项
            if hasattr(self.world, 'interface_manager') and self.world.interface_manager and hasattr(self.world.interface_manager, 'treeWidget') and self.world.interface_manager.treeWidget:
                #print("查找树形控件中的对应项...")
                root = self.world.interface_manager.treeWidget.invisibleRootItem()
                foundItem = None

                for i in range(root.childCount()):
                    sceneItem = root.child(i)
                    if sceneItem.text(0) == "场景":
                        #print(f"在场景节点下查找...")
                        foundItem = self.world.interface_manager.findTreeItem(selectedModel, sceneItem)
                        if foundItem:
                            print(f"✓ 在树形控件中找到对应项: {foundItem.text(0)}")
                            try:
                                self.world.interface_manager.treeWidget.itemClicked.disconnect()
                            except TypeError:
                                pass

                            self.world.interface_manager.treeWidget.setCurrentItem(foundItem)

                            self.world.interface_manager.treeWidget.itemClicked.connect(
                                self.world.interface_manager.onTreeItemClicked)
                        else:
                            print("× 在树形控件中没有找到对应项")
                        break

                if not foundItem:
                    print("× 没有找到场景节点或对应的树形项")
            else:
                print("× 树形控件不存在")
        else:
            print("× 没有找到可选择的模型节点")
            self.world.selection.updateSelection(None)
    
    def mouseReleaseEventLeft(self, evt):
        """处理鼠标左键释放事件"""
        # 处理坐标轴拖拽结束
        if self.world.selection.isDraggingGizmo:
            self.world.selection.stopGizmoDrag()
            return
    
    def wheelForward(self, data=None):
        """处理滚轮向前滚动（前进）"""
        # 调用CoreWorld的父类方法
        super(type(self.world), self.world).wheelForward(data)
        
        # 更新属性面板
        if (self.world.interface_manager.treeWidget and 
            self.world.interface_manager.treeWidget.currentItem() and 
            self.world.interface_manager.treeWidget.currentItem().text(0) == "相机"):
            self.world.property_panel.updatePropertyPanel(
                self.world.interface_manager.treeWidget.currentItem())
    
    def wheelBackward(self, data=None):
        """处理滚轮向后滚动（后退）"""
        # 调用CoreWorld的父类方法
        super(type(self.world), self.world).wheelBackward(data)
        
        # 更新属性面板
        if (self.world.interface_manager.treeWidget and 
            self.world.interface_manager.treeWidget.currentItem() and 
            self.world.interface_manager.treeWidget.currentItem().text(0) == "相机"):
            self.world.property_panel.updatePropertyPanel(
                self.world.interface_manager.treeWidget.currentItem())
    
    def mousePressEventMiddle(self, evt):
        """处理鼠标中键按下事件"""
        # 已移除原有的Z轴拖拽功能
        pass
    
    def mouseReleaseEventMiddle(self, evt):
        """处理鼠标中键释放事件"""
        # 已移除原有的Z轴拖拽功能
        pass
    
    def mouseMoveEvent(self, evt):
        """处理鼠标移动事件"""
        if not evt:
            return
            
        # 处理坐标轴拖拽
        if self.world.selection.isDraggingGizmo:
            x = evt.get('x', 0)
            y = evt.get('y', 0)
            
            # 获取准确的窗口尺寸
            winWidth, winHeight = self.world.getWindowSize()
            
            # 将屏幕坐标转换为世界坐标
            mx = 2.0 * x / float(winWidth) - 1.0
            my = 1.0 - 2.0 * y / float(winHeight)
            
            # 更新坐标轴拖拽
            self.world.selection.updateGizmoDrag(x, y)
            return
        
        # 更新坐标轴高亮（鼠标悬停效果）
        if self.world.selection.gizmo and not self.world.selection.isDraggingGizmo:
            x = evt.get('x', 0)
            y = evt.get('y', 0)
            # 减少高亮调试输出，只在需要时输出
            # 已静默处理，避免控制台刷屏
            self.world.selection.updateGizmoHighlight(x, y)
        
        # 调用CoreWorld的父类方法处理基础的相机旋转
        super(type(self.world), self.world).mouseMoveEvent(evt) 