"""
VR交互系统模块

提供VR手柄与3D场景的交互功能：
- 射线投射和碰撞检测
- 对象选择和高亮
- 对象抓取和移动
- UI交互
- 距离抓取
"""

from panda3d.core import (
    Vec3, Vec4, Mat4, Point3, CollisionRay, CollisionTraverser,
    CollisionNode, CollisionHandlerQueue, BitMask32, NodePath,
    CollisionSphere, CollisionTube, RenderState, TransparencyAttrib,
    ColorAttrib
)
from direct.showbase.DirectObject import DirectObject


class VRInteractionManager(DirectObject):
    """VR交互管理器 - 处理手柄与场景的交互"""

    def __init__(self, vr_manager):
        """初始化VR交互管理器

        Args:
            vr_manager: VR管理器实例
        """
        super().__init__()

        self.vr_manager = vr_manager
        self.world = vr_manager.world if hasattr(vr_manager, 'world') else None

        # 碰撞检测系统
        self.collision_traverser = CollisionTraverser()
        self.collision_queue = CollisionHandlerQueue()

        # 射线投射节点
        self.left_ray_node = None
        self.right_ray_node = None
        self.ray_collision_nodes = {}

        # 选择和抓取状态
        self.selected_objects = {}  # 控制器 -> 选中对象
        self.grabbed_objects = {}   # 控制器 -> 抓取对象
        self.grab_offsets = {}      # 控制器 -> 抓取偏移

        # 交互参数
        self.selection_range = 50.0  # 选择距离
        self.grab_threshold = 0.5    # 抓取扳机阈值
        self.selection_color = Vec4(0.9, 0.9, 0.2, 1.0)  # 选择高亮颜色
        self.grab_color = Vec4(0.2, 0.9, 0.2, 1.0)       # 抓取高亮颜色

        # 高亮状态
        self.highlighted_objects = set()
        self.original_colors = {}    # 存储对象原始颜色

        print("✓ VR交互管理器初始化完成")

    def initialize(self):
        """初始化交互系统"""
        try:
            print("🔧 正在初始化VR交互系统...")

            # 创建射线投射节点
            self._create_ray_casters()

            # 设置碰撞检测
            self._setup_collision_detection()

            print("✅ VR交互系统初始化成功")
            return True

        except Exception as e:
            print(f"❌ VR交互系统初始化失败: {e}")
            import traceback
            traceback.print_exc()
            return False

    def _create_ray_casters(self):
        """创建射线投射节点"""
        # 为左手控制器创建射线
        if self.vr_manager.left_controller and self.vr_manager.left_controller.anchor_node:
            self.left_ray_node = self._create_controller_ray('left', self.vr_manager.left_controller.anchor_node)

        # 为右手控制器创建射线
        if self.vr_manager.right_controller and self.vr_manager.right_controller.anchor_node:
            self.right_ray_node = self._create_controller_ray('right', self.vr_manager.right_controller.anchor_node)

    def _create_controller_ray(self, controller_name, anchor_node):
        """为控制器创建射线投射节点"""
        # 创建射线碰撞体
        ray = CollisionRay()
        ray.setOrigin(0, 0, 0)  # 从控制器原点开始
        ray.setDirection(0, 1, 0)  # 沿Y轴正方向

        # 创建碰撞节点
        ray_collision_node = CollisionNode(f'{controller_name}_ray')
        ray_collision_node.addSolid(ray)

        # 设置碰撞掩码
        ray_collision_node.setFromCollideMask(BitMask32.bit(0))  # 射线掩码
        ray_collision_node.setIntoCollideMask(BitMask32.allOff())  # 不接受碰撞

        # 附加到控制器锚点
        ray_node = anchor_node.attachNewNode(ray_collision_node)
        self.ray_collision_nodes[controller_name] = ray_collision_node

        # 注册到碰撞遍历器
        self.collision_traverser.addCollider(ray_node, self.collision_queue)

        print(f"✓ {controller_name}手控制器射线投射已创建")
        return ray_node

    def _setup_collision_detection(self):
        """设置碰撞检测系统"""
        if self.world:
            # 使用世界的碰撞系统
            if hasattr(self.world, 'render'):
                # 为所有可交互对象设置碰撞体
                self._setup_scene_collision_objects()
        else:
            print("⚠️ 无法访问世界对象，跳过场景碰撞设置")

    def _setup_scene_collision_objects(self):
        """为场景对象设置碰撞体"""
        if not self.world or not hasattr(self.world, 'render'):
            return

        try:
            # 遍历场景中的所有节点，为它们添加碰撞体
            for node_path in self.world.render.findAllMatches("**/+GeomNode"):
                self._add_collision_to_object(node_path)

        except Exception as e:
            print(f"⚠️ 设置场景碰撞对象失败: {e}")

    def _add_collision_to_object(self, node_path):
        """为对象添加碰撞体"""
        try:
            # 获取对象的边界框
            bounds = node_path.getBounds()
            if bounds.isEmpty():
                return

            # 计算边界球
            center = bounds.getCenter()
            radius = bounds.getRadius()

            # 创建球形碰撞体
            collision_sphere = CollisionSphere(center, radius)

            # 创建碰撞节点
            collision_node = CollisionNode(f'{node_path.getName()}_collision')
            collision_node.addSolid(collision_sphere)

            # 设置碰撞掩码
            collision_node.setIntoCollideMask(BitMask32.bit(0))  # 接受射线碰撞
            collision_node.setFromCollideMask(BitMask32.allOff())  # 不发射射线

            # 附加碰撞节点
            collision_node_path = node_path.attachNewNode(collision_node)

            # 标记为可交互对象
            node_path.setTag('interactable', 'true')
            node_path.setTag('original_name', node_path.getName())

        except Exception as e:
            print(f"⚠️ 为对象 {node_path.getName()} 添加碰撞体失败: {e}")

    def update(self):
        """更新交互系统 - 每帧调用"""
        if not self.vr_manager.are_controllers_connected():
            return

        # 执行碰撞检测
        self._perform_collision_detection()

        # 更新选择状态
        self._update_selections()

        # 更新抓取状态
        self._update_grabbing()

    def _perform_collision_detection(self):
        """执行碰撞检测"""
        if self.world and hasattr(self.world, 'render'):
            self.collision_traverser.traverse(self.world.render)

    def _update_selections(self):
        """更新对象选择状态"""
        # 清除之前的选择高亮
        self._clear_selection_highlights()

        # 检查每个控制器的选择
        for controller in self.vr_manager.get_connected_controllers():
            if not controller:
                continue

            # 获取最近的碰撞对象
            hit_object = self._get_closest_hit_object(controller.name)

            if hit_object:
                # 高亮选中的对象
                self._highlight_object(hit_object, self.selection_color)
                self.selected_objects[controller.name] = hit_object

                # 显示控制器射线
                controller.show_ray(True)
                controller.set_ray_color([0.9, 0.9, 0.2, 0.8])  # 黄色
            else:
                # 没有选中对象
                if controller.name in self.selected_objects:
                    del self.selected_objects[controller.name]

                # 隐藏射线（除非正在抓取）
                if controller.name not in self.grabbed_objects:
                    controller.show_ray(False)

    def _get_closest_hit_object(self, controller_name):
        """获取指定控制器射线最近的碰撞对象"""
        if controller_name not in self.ray_collision_nodes:
            return None

        closest_object = None
        closest_distance = float('inf')

        # 检查碰撞队列中的条目
        for i in range(self.collision_queue.getNumEntries()):
            entry = self.collision_queue.getEntry(i)

            # 检查是否是该控制器的射线
            from_node = entry.getFromNodePath()
            if from_node.node() == self.ray_collision_nodes[controller_name]:
                # 获取碰撞的对象
                hit_node_path = entry.getIntoNodePath()

                # 获取实际的几何对象（父节点）
                geom_object = hit_node_path.getParent()
                if geom_object and geom_object.hasTag('interactable'):
                    distance = entry.getSurfacePoint(geom_object).length()

                    if distance < closest_distance and distance <= self.selection_range:
                        closest_distance = distance
                        closest_object = geom_object

        return closest_object

    def _update_grabbing(self):
        """更新对象抓取状态"""
        for controller in self.vr_manager.get_connected_controllers():
            if not controller:
                continue

            controller_name = controller.name

            # 检查是否按下抓取按钮
            if controller.is_trigger_pressed(threshold=self.grab_threshold):
                # 如果还没有抓取对象
                if controller_name not in self.grabbed_objects:
                    # 尝试抓取选中的对象
                    if controller_name in self.selected_objects:
                        selected_obj = self.selected_objects[controller_name]
                        self._start_grab(controller, selected_obj)

                # 如果正在抓取，更新对象位置
                if controller_name in self.grabbed_objects:
                    self._update_grabbed_object(controller)

            else:
                # 释放抓取
                if controller_name in self.grabbed_objects:
                    self._release_grab(controller)

    def _start_grab(self, controller, obj):
        """开始抓取对象"""
        controller_name = controller.name

        try:
            # 计算抓取偏移（对象相对于控制器的位置）
            controller_pos = controller.get_world_position()
            object_pos = obj.getPos(self.world.render if self.world else obj.getParent())

            offset = object_pos - controller_pos
            self.grab_offsets[controller_name] = offset

            # 记录抓取状态
            self.grabbed_objects[controller_name] = obj

            # 改变对象颜色表示抓取状态
            self._highlight_object(obj, self.grab_color)

            # 触发震动反馈
            controller.trigger_haptic_feedback(0.01, 0.8)

            # 显示绿色射线表示抓取
            controller.show_ray(True)
            controller.set_ray_color([0.2, 0.9, 0.2, 0.8])

            print(f"🤏 {controller_name}手开始抓取对象: {obj.getName()}")

        except Exception as e:
            print(f"⚠️ 开始抓取失败: {e}")

    def _update_grabbed_object(self, controller):
        """更新被抓取对象的位置"""
        controller_name = controller.name

        if controller_name not in self.grabbed_objects:
            return

        try:
            grabbed_obj = self.grabbed_objects[controller_name]
            grab_offset = self.grab_offsets.get(controller_name, Vec3(0, 0, 0))

            # 计算新位置
            controller_pos = controller.get_world_position()
            new_pos = controller_pos + grab_offset

            # 更新对象位置
            grabbed_obj.setPos(self.world.render if self.world else grabbed_obj.getParent(), new_pos)

            # 可选：同步旋转
            if hasattr(controller, 'get_world_rotation'):
                controller_rot = controller.get_world_rotation()
                grabbed_obj.setHpr(self.world.render if self.world else grabbed_obj.getParent(), controller_rot)

        except Exception as e:
            print(f"⚠️ 更新抓取对象失败: {e}")

    def _release_grab(self, controller):
        """释放抓取的对象"""
        controller_name = controller.name

        if controller_name not in self.grabbed_objects:
            return

        try:
            grabbed_obj = self.grabbed_objects[controller_name]

            # 恢复对象原始颜色
            self._restore_object_color(grabbed_obj)

            # 清理抓取状态
            del self.grabbed_objects[controller_name]
            if controller_name in self.grab_offsets:
                del self.grab_offsets[controller_name]

            # 触发震动反馈
            controller.trigger_haptic_feedback(0.005, 0.4)

            print(f"🫳 {controller_name}手释放对象: {grabbed_obj.getName()}")

        except Exception as e:
            print(f"⚠️ 释放抓取失败: {e}")

    def _highlight_object(self, obj, color):
        """高亮显示对象"""
        if obj in self.highlighted_objects:
            return

        try:
            # 保存原始颜色
            if obj not in self.original_colors:
                self.original_colors[obj] = obj.getColor()

            # 设置高亮颜色
            obj.setColor(color)
            self.highlighted_objects.add(obj)

        except Exception as e:
            print(f"⚠️ 高亮对象失败: {e}")

    def _restore_object_color(self, obj):
        """恢复对象原始颜色"""
        if obj not in self.highlighted_objects:
            return

        try:
            # 恢复原始颜色
            if obj in self.original_colors:
                obj.setColor(self.original_colors[obj])
                del self.original_colors[obj]

            self.highlighted_objects.discard(obj)

        except Exception as e:
            print(f"⚠️ 恢复对象颜色失败: {e}")

    def _clear_selection_highlights(self):
        """清除所有选择高亮"""
        for obj in list(self.highlighted_objects):
            # 只清除非抓取状态的对象
            is_grabbed = any(obj == grabbed_obj for grabbed_obj in self.grabbed_objects.values())
            if not is_grabbed:
                self._restore_object_color(obj)

    def get_selected_object(self, controller_name):
        """获取指定控制器选中的对象"""
        return self.selected_objects.get(controller_name)

    def get_grabbed_object(self, controller_name):
        """获取指定控制器抓取的对象"""
        return self.grabbed_objects.get(controller_name)

    def is_grabbing(self, controller_name):
        """检查指定控制器是否正在抓取对象"""
        return controller_name in self.grabbed_objects

    def force_release_all(self):
        """强制释放所有抓取的对象"""
        for controller in self.vr_manager.get_connected_controllers():
            if controller and controller.name in self.grabbed_objects:
                self._release_grab(controller)

    def cleanup(self):
        """清理资源"""
        self.ignoreAll()

        # 释放所有抓取
        self.force_release_all()

        # 清理碰撞系统
        self.collision_traverser.clearColliders()
        self.ray_collision_nodes.clear()

        # 清理高亮状态
        for obj in list(self.highlighted_objects):
            self._restore_object_color(obj)

        print("🧹 VR交互管理器已清理")