#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
测试坐标轴拖拽功能
验证物体移动是否正常工作
"""

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

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QLabel, QPushButton, QHBoxLayout
from PyQt5.QtCore import Qt
from QMeta3D.QMeta3DWidget import QMeta3DWidget
from QMeta3D.Meta3DWorld import Meta3DWorld
from panda3d.core import *
from direct.task import Task

class GizmoDragTestWorld(Meta3DWorld):
    def __init__(self):
        super().__init__()
        
        print("=== 坐标轴拖拽测试 ===")
        self.setBackgroundColor(0.2, 0.2, 0.3)
        
        # 调整相机位置
        self.cam.setPos(10, -20, 10)
        self.cam.lookAt(0, 0, 0)
        
        # Qt部件引用
        self.qtWidget = None
        
        # 添加基础光照
        alight = AmbientLight('alight')
        alight.setColor((0.3, 0.3, 0.3, 1))
        alnp = self.render.attachNewNode(alight)
        self.render.setLight(alnp)
        
        dlight = DirectionalLight('dlight')
        dlight.setColor((0.8, 0.8, 0.8, 1))
        dlnp = self.render.attachNewNode(dlight)
        dlnp.setHpr(45, -45, 0)
        self.render.setLight(dlnp)
        
        # 创建测试立方体
        self.createTestCube()
        
        # 坐标轴相关变量（复制自主程序）
        self.gizmo = None
        self.gizmoTarget = None
        self.gizmoXAxis = None
        self.gizmoYAxis = None
        self.gizmoZAxis = None
        self.axis_length = 3.0
        
        self.isDraggingGizmo = False
        self.dragGizmoAxis = None
        self.dragStartMousePos = None
        self.gizmoTargetStartPos = None
        self.gizmoHighlightAxis = None
        
        self.gizmo_colors = {
            "x": (1, 0, 0, 1),  # 红色
            "y": (0, 1, 0, 1),  # 绿色
            "z": (0, 0, 1, 1)   # 蓝色
        }
        
        self.gizmo_highlight_colors = {
            "x": (1, 1, 0, 1),  # 黄色高亮
            "y": (1, 1, 0, 1),
            "z": (1, 1, 0, 1)
        }
        
        # 当前工具
        self.currentTool = "移动"
        
        # 自动选中立方体并创建坐标轴
        self.selectedNode = self.testCube
        self.createGizmo(self.testCube)
        
        print("测试说明:")
        print("1. 立方体已选中，坐标轴已显示")
        print("2. 鼠标悬停在坐标轴上应该变为黄色")
        print("3. 点击并拖拽坐标轴应该移动立方体")
        print("4. 观看控制台输出以了解拖拽过程")
        
    def setQtWidget(self, widget):
        """设置Qt部件引用"""
        self.qtWidget = widget
        print(f"✓ 设置Qt部件引用")
    
    def getWindowSize(self):
        """获取准确的窗口尺寸"""
        if self.qtWidget:
            width, height = self.qtWidget.getActualSize()
            if width > 0 and height > 0:
                return width, height
        
        if hasattr(self, 'win') and self.win:
            width = self.win.getXSize()
            height = self.win.getYSize()
            return width, height
        
        return 800, 600
        
    def createTestCube(self):
        """创建测试立方体"""
        from panda3d.core import CardMaker
        
        # 创建一个简单的立方体
        cm = CardMaker('cube')
        cm.setFrame(-1, 1, -1, 1)
        
        # 创建6个面
        self.testCube = self.render.attachNewNode("testCube")
        
        # 前面（红色）
        front = self.testCube.attachNewNode(cm.generate())
        front.setPos(0, 1, 0)
        front.setColor(1, 0.5, 0.5, 1)
        
        # 后面（绿色）
        back = self.testCube.attachNewNode(cm.generate())
        back.setPos(0, -1, 0)
        back.setHpr(0, 0, 180)
        back.setColor(0.5, 1, 0.5, 1)
        
        # 左面（蓝色）
        left = self.testCube.attachNewNode(cm.generate())
        left.setPos(-1, 0, 0)
        left.setHpr(0, 0, 90)
        left.setColor(0.5, 0.5, 1, 1)
        
        # 右面（黄色）
        right = self.testCube.attachNewNode(cm.generate())
        right.setPos(1, 0, 0)
        right.setHpr(0, 0, -90)
        right.setColor(1, 1, 0.5, 1)
        
        # 顶面（紫色）
        top = self.testCube.attachNewNode(cm.generate())
        top.setPos(0, 0, 1)
        top.setHpr(-90, 0, 0)
        top.setColor(1, 0.5, 1, 1)
        
        # 底面（青色）
        bottom = self.testCube.attachNewNode(cm.generate())
        bottom.setPos(0, 0, -1)
        bottom.setHpr(90, 0, 0)
        bottom.setColor(0.5, 1, 1, 1)
        
        print(f"✓ 创建测试立方体，位置: {self.testCube.getPos()}")
        return self.testCube
    
    # 从主程序复制的坐标轴相关方法
    def createGizmo(self, nodePath):
        """为选中的节点创建坐标轴工具"""
        try:
            if self.gizmo:
                self.gizmo.removeNode()
                self.gizmo = None
            
            if not nodePath:
                return
                
            self.gizmo = self.render.attachNewNode("gizmo")
            self.gizmoTarget = nodePath
            
            # 将坐标轴放在实体上方
            bounds = nodePath.getBounds()
            if bounds and not bounds.isEmpty():
                center = bounds.getCenter()
                maxPoint = bounds.getMax()
                gizmo_pos = Point3(center.x, center.y, maxPoint.z + 2.0)
                self.gizmo.setPos(gizmo_pos)
            
            self.createGizmoGeometry()
            print(f"✓ 为节点 {nodePath.getName()} 创建了坐标轴")
            
        except Exception as e:
            print(f"创建坐标轴失败: {str(e)}")
    
    def createGizmoGeometry(self):
        """创建坐标轴的几何体"""
        try:
            if not self.gizmo:
                return
            
            from panda3d.core import LineSegs
            
            # 创建X轴（红色）
            x_lines = LineSegs()
            x_lines.setThickness(4.0)
            x_lines.setColor(1, 0, 0, 1)
            x_lines.moveTo(0, 0, 0)
            x_lines.drawTo(self.axis_length, 0, 0)
            # 箭头
            x_lines.moveTo(self.axis_length - 0.5, -0.2, 0)
            x_lines.drawTo(self.axis_length, 0, 0)
            x_lines.drawTo(self.axis_length - 0.5, 0.2, 0)
            
            self.gizmoXAxis = self.gizmo.attachNewNode(x_lines.create())
            self.gizmoXAxis.setName("gizmo_x_axis")
            
            # 创建Y轴（绿色）
            y_lines = LineSegs()
            y_lines.setThickness(4.0)
            y_lines.setColor(0, 1, 0, 1)
            y_lines.moveTo(0, 0, 0)
            y_lines.drawTo(0, self.axis_length, 0)
            # 箭头
            y_lines.moveTo(-0.2, self.axis_length - 0.5, 0)
            y_lines.drawTo(0, self.axis_length, 0)
            y_lines.drawTo(0.2, self.axis_length - 0.5, 0)
            
            self.gizmoYAxis = self.gizmo.attachNewNode(y_lines.create())
            self.gizmoYAxis.setName("gizmo_y_axis")
            
            # 创建Z轴（蓝色）
            z_lines = LineSegs()
            z_lines.setThickness(4.0)
            z_lines.setColor(0, 0, 1, 1)
            z_lines.moveTo(0, 0, 0)
            z_lines.drawTo(0, 0, self.axis_length)
            # 箭头
            z_lines.moveTo(-0.2, 0, self.axis_length - 0.5)
            z_lines.drawTo(0, 0, self.axis_length)
            z_lines.drawTo(0.2, 0, self.axis_length - 0.5)
            
            self.gizmoZAxis = self.gizmo.attachNewNode(z_lines.create())
            self.gizmoZAxis.setName("gizmo_z_axis")
            
            # 确保坐标轴不被光照影响
            self.gizmoXAxis.setLightOff()
            self.gizmoYAxis.setLightOff()
            self.gizmoZAxis.setLightOff()
            
        except Exception as e:
            print(f"创建坐标轴几何体失败: {str(e)}")
    
    # 主程序的鼠标事件处理方法（简化版）
    def mousePressEventLeft(self, evt):
        """处理鼠标左键按下事件"""
        print(f"\n=== 鼠标左键点击 ===")
        
        if not evt:
            return
            
        x = evt.get('x', 0)
        y = evt.get('y', 0)
        print(f"鼠标位置: ({x}, {y})")
        
        # 检查是否点击了坐标轴
        if self.currentTool == "移动" and self.gizmo:
            gizmoAxis = self.checkGizmoClick(x, y)
            if gizmoAxis:
                print(f"✓ 检测到坐标轴点击: {gizmoAxis}")
                self.startGizmoDrag(gizmoAxis, x, y)
                return
            else:
                print("× 没有点击到坐标轴")
    
    def mouseReleaseEventLeft(self, evt):
        """处理鼠标左键释放事件"""
        if self.isDraggingGizmo:
            self.stopGizmoDrag()
    
    def mouseMoveEvent(self, evt):
        """处理鼠标移动事件"""
        if not evt:
            return
            
        # 处理坐标轴拖拽
        if self.isDraggingGizmo:
            x = evt.get('x', 0)
            y = evt.get('y', 0)
            self.updateGizmoDrag(x, y)
            return
        
        # 更新坐标轴高亮
        if self.currentTool == "移动" and self.gizmo and not self.isDraggingGizmo:
            x = evt.get('x', 0)
            y = evt.get('y', 0)
            self.updateGizmoHighlight(x, y)
    
    # 从主程序复制的方法（这里需要包含所有坐标轴相关的方法）
    # [为了简洁，这里只展示核心方法，实际应该包含所有方法]
    
    def checkGizmoClick(self, mouseX, mouseY):
        """简化的坐标轴点击检测"""
        if not self.gizmo or not self.gizmoTarget:
            return None
            
        try:
            print(f"检查坐标轴点击: 鼠标({mouseX}, {mouseY})")
            
            # 获取坐标轴中心的世界坐标
            gizmo_world_pos = self.gizmo.getPos(self.render)
            print(f"坐标轴世界位置: {gizmo_world_pos}")
            
            # 计算各轴端点的世界坐标
            x_end = gizmo_world_pos + Vec3(self.axis_length, 0, 0)
            y_end = gizmo_world_pos + Vec3(0, self.axis_length, 0) 
            z_end = gizmo_world_pos + Vec3(0, 0, self.axis_length)
            
            def worldToScreen(world_pos):
                cam_pos = self.cam.getRelativePoint(self.render, world_pos)
                if cam_pos.getY() <= 0:
                    return None
                
                screen_pos = Point2()
                if self.cam.node().getLens().project(cam_pos, screen_pos):
                    win_width, win_height = self.getWindowSize()
                    win_x = (screen_pos.getX() + 1.0) * 0.5 * win_width
                    win_y = (1.0 - screen_pos.getY()) * 0.5 * win_height
                    return (win_x, win_y)
                return None
            
            center_screen = worldToScreen(gizmo_world_pos)
            x_screen = worldToScreen(x_end)
            y_screen = worldToScreen(y_end)
            z_screen = worldToScreen(z_end)
            
            if not center_screen:
                return None
            
            print(f"中心屏幕坐标: {center_screen}")
            print(f"X轴端点屏幕坐标: {x_screen}")
            
            click_threshold = 30
            
            # 简化的距离检测
            def distance_to_line(point, line_start, line_end):
                import math
                px, py = point
                x1, y1 = line_start
                x2, y2 = line_end
                
                line_length_sq = (x2 - x1) ** 2 + (y2 - y1) ** 2
                if line_length_sq < 0.001:
                    return math.sqrt((px - x1) ** 2 + (py - y1) ** 2)
                
                t = max(0, min(1, ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / line_length_sq))
                projection_x = x1 + t * (x2 - x1)
                projection_y = y1 + t * (y2 - y1)
                
                distance = math.sqrt((px - projection_x) ** 2 + (py - projection_y) ** 2)
                return distance
            
            # 检测各个轴
            axes_data = [
                ("x", x_screen, "X轴"),
                ("y", y_screen, "Y轴"), 
                ("z", z_screen, "Z轴")
            ]
            
            for axis_name, axis_screen, axis_label in axes_data:
                if axis_screen:
                    distance = distance_to_line(
                        (mouseX, mouseY), center_screen, axis_screen
                    )
                    #print(f"{axis_label}距离: {distance:.2f}")
                    
                    if distance < click_threshold:
                        print(f"✓ 点击了{axis_label}")
                        return axis_name
            
            print("× 没有点击任何轴")
            return None
            
        except Exception as e:
            print(f"坐标轴点击检测失败: {str(e)}")
            return None
    
    def startGizmoDrag(self, axis, mouseX, mouseY):
        """开始坐标轴拖拽"""
        try:
            self.isDraggingGizmo = True
            self.dragGizmoAxis = axis
            self.dragStartMousePos = (mouseX, mouseY)
            
            # 保存开始拖拽时目标节点的位置
            if self.gizmoTarget:
                self.gizmoTargetStartPos = self.gizmoTarget.getPos()
            
            print(f"✓ 开始拖拽 {axis} 轴，开始位置: {self.gizmoTargetStartPos}")
            
        except Exception as e:
            print(f"开始坐标轴拖拽失败: {str(e)}")
    
    def updateGizmoDrag(self, mouseX, mouseY):
        """更新坐标轴拖拽"""
        try:
            if not self.isDraggingGizmo or not self.gizmoTarget or not hasattr(self, 'dragStartMousePos'):
                return
                
            # 计算鼠标移动距离（屏幕像素）
            mouseDeltaX = mouseX - self.dragStartMousePos[0]
            mouseDeltaY = mouseY - self.dragStartMousePos[1]
            
            # 获取坐标轴在屏幕空间的方向向量
            gizmo_world_pos = self.gizmoTargetStartPos
            
            if self.dragGizmoAxis == "x":
                axis_end = gizmo_world_pos + Vec3(1, 0, 0)
            elif self.dragGizmoAxis == "y":
                axis_end = gizmo_world_pos + Vec3(0, 1, 0)
            elif self.dragGizmoAxis == "z":
                axis_end = gizmo_world_pos + Vec3(0, 0, 1)
            else:
                return
            
            # 投影到屏幕空间
            def worldToScreen(worldPos):
                # 先转换为相机坐标系
                camPos = self.cam.getRelativePoint(self.render, worldPos)
                
                # 检查是否在相机前方
                if camPos.getY() <= 0:
                    return None
                
                screenPos = Point2()
                if self.cam.node().getLens().project(camPos, screenPos):
                    # 获取准确的窗口尺寸
                    winWidth, winHeight = self.getWindowSize()
                    
                    winX = (screenPos.x + 1) * 0.5 * winWidth
                    winY = (1 - screenPos.y) * 0.5 * winHeight
                    return (winX, winY)
                return None
            
            gizmo_screen = worldToScreen(gizmo_world_pos)
            axis_screen = worldToScreen(axis_end)
            
            if not gizmo_screen or not axis_screen:
                return
            
            # 计算轴在屏幕空间的方向向量
            screen_axis_dir = (
                axis_screen[0] - gizmo_screen[0],
                axis_screen[1] - gizmo_screen[1]
            )
            
            # 归一化屏幕轴方向
            import math
            length = math.sqrt(screen_axis_dir[0]**2 + screen_axis_dir[1]**2)
            if length > 0:
                screen_axis_dir = (screen_axis_dir[0] / length, screen_axis_dir[1] / length)
            else:
                return
            
            # 将鼠标移动投影到轴方向上
            projected_distance = (mouseDeltaX * screen_axis_dir[0] + 
                                mouseDeltaY * screen_axis_dir[1])
            
            # 转换投影距离为世界坐标移动距离
            scale_factor = 0.01  # 可以调整这个值来改变拖拽灵敏度
            
            if self.dragGizmoAxis == "x":
                movement = Vec3(projected_distance * scale_factor, 0, 0)
            elif self.dragGizmoAxis == "y":
                movement = Vec3(0, projected_distance * scale_factor, 0)
            elif self.dragGizmoAxis == "z":
                movement = Vec3(0, 0, projected_distance * scale_factor)
            
            # 应用移动到目标节点
            newPos = self.gizmoTargetStartPos + movement
            self.gizmoTarget.setPos(newPos)
            
            # 添加调试输出（只在前几次输出，避免刷屏）
            if not hasattr(self, '_drag_debug_count'):
                self._drag_debug_count = 0
            if self._drag_debug_count < 3:
                print(f"拖拽调试 - 轴: {self.dragGizmoAxis}")
                print(f"  鼠标移动: ({mouseDeltaX:.1f}, {mouseDeltaY:.1f})")
                print(f"  投影距离: {projected_distance:.3f}")
                print(f"  世界移动: {movement}")
                print(f"  开始位置: {self.gizmoTargetStartPos}")
                print(f"  新位置: {newPos}")
                print(f"  实际设置位置: {self.gizmoTarget.getPos()}")
                self._drag_debug_count += 1
            
        except Exception as e:
            print(f"更新坐标轴拖拽失败: {str(e)}")
    
    def stopGizmoDrag(self):
        """停止坐标轴拖拽"""
        self.isDraggingGizmo = False
        self.dragGizmoAxis = None
        self.dragStartMousePos = None
        self.gizmoTargetStartPos = None
        
        # 重置调试计数器
        if hasattr(self, '_drag_debug_count'):
            self._drag_debug_count = 0
            
        print("✓ 停止坐标轴拖拽")
    
    def updateGizmoHighlight(self, mouseX, mouseY):
        """更新坐标轴高亮状态（简化版）"""
        if not self.gizmo or self.isDraggingGizmo:
            return
            
        # 检测鼠标悬停的轴
        hoveredAxis = self.checkGizmoClick(mouseX, mouseY)
        
        # 如果高亮状态发生变化
        if hoveredAxis != self.gizmoHighlightAxis:
            # 恢复之前高亮的轴
            if self.gizmoHighlightAxis:
                self.setGizmoAxisColor(self.gizmoHighlightAxis, self.gizmo_colors[self.gizmoHighlightAxis])
            
            # 高亮新的轴
            if hoveredAxis:
                self.setGizmoAxisColor(hoveredAxis, self.gizmo_highlight_colors[hoveredAxis])
                
            self.gizmoHighlightAxis = hoveredAxis
    
    def setGizmoAxisColor(self, axis, color):
        """设置坐标轴颜色"""
        try:
            if axis == "x" and self.gizmoXAxis:
                self.gizmoXAxis.setColor(*color)
            elif axis == "y" and self.gizmoYAxis:
                self.gizmoYAxis.setColor(*color)
            elif axis == "z" and self.gizmoZAxis:
                self.gizmoZAxis.setColor(*color)
        except Exception as e:
            print(f"设置坐标轴颜色失败: {str(e)}")


class CustomGizmoDragTestWidget(QMeta3DWidget):
    """支持坐标轴拖拽测试的部件"""
    def __init__(self, world, parent=None):
        super().__init__(world, parent)
        self.world = world
        
        if hasattr(world, 'setQtWidget'):
            world.setQtWidget(self)
    
    def getActualSize(self):
        return (self.width(), self.height())
    
    def mousePressEvent(self, event):
        """处理Qt鼠标按下事件"""
        if event.button() == Qt.LeftButton:
            self.world.mousePressEventLeft({
                'x': event.x(),
                'y': event.y()
            })
        event.accept()
    
    def mouseReleaseEvent(self, event):
        """处理Qt鼠标释放事件"""
        if event.button() == Qt.LeftButton:
            self.world.mouseReleaseEventLeft({
                'x': event.x(),
                'y': event.y()
            })
        event.accept()
    
    def mouseMoveEvent(self, event):
        """处理Qt鼠标移动事件"""
        self.world.mouseMoveEvent({
            'x': event.x(),
            'y': event.y()
        })
        event.accept()


def main():
    print("启动坐标轴拖拽测试...")
    
    app = QApplication(sys.argv)
    
    # 创建主窗口
    mainWindow = QMainWindow()
    mainWindow.setWindowTitle("坐标轴拖拽测试")
    mainWindow.setGeometry(100, 100, 900, 700)
    
    # 创建布局
    centralWidget = QWidget()
    layout = QVBoxLayout(centralWidget)
    
    # 添加说明
    infoLayout = QHBoxLayout()
    label = QLabel("坐标轴拖拽测试 - 立方体已选中，尝试拖拽红色/绿色/蓝色坐标轴")
    label.setAlignment(Qt.AlignCenter)
    label.setStyleSheet("background-color: lightcyan; padding: 10px; font-size: 12px;")
    infoLayout.addWidget(label)
    
    resetBtn = QPushButton("重置立方体位置")
    infoLayout.addWidget(resetBtn)
    
    layout.addLayout(infoLayout)
    
    # 创建世界
    world = GizmoDragTestWorld()
    
    # 连接重置按钮
    def resetCube():
        world.testCube.setPos(0, 0, 0)
        print("立方体位置已重置")
    resetBtn.clicked.connect(resetCube)
    
    # 创建测试部件
    pandaWidget = CustomGizmoDragTestWidget(world)
    layout.addWidget(pandaWidget)
    
    mainWindow.setCentralWidget(centralWidget)
    mainWindow.show()
    
    print("\n使用说明:")
    print("1. 立方体已选中，红/绿/蓝坐标轴应该可见")
    print("2. 鼠标悬停在坐标轴上应该变为黄色")
    print("3. 点击并拖拽坐标轴，立方体应该沿对应轴移动")
    print("4. 观察控制台输出了解拖拽过程")
    
    return app.exec_()

if __name__ == "__main__":
    sys.exit(main()) 