"""
粒子工具函数
提供粒子系统相关的实用工具函数
"""

import math
import random
from typing import List, Tuple, Optional
from panda3d.core import Vec3, Point3, LVector3f, Quat

class ParticleUtils:
    """
    粒子系统工具类
    提供各种实用的粒子计算和生成函数
    """
    
    @staticmethod
    def lerp(a: float, b: float, t: float) -> float:
        """
        线性插值
        
        Args:
            a: 起始值
            b: 结束值
            t: 插值参数 (0-1)
            
        Returns:
            插值结果
        """
        return a + (b - a) * t
        
    @staticmethod
    def lerp_vec3(a: Vec3, b: Vec3, t: float) -> Vec3:
        """
        Vec3线性插值
        
        Args:
            a: 起始向量
            b: 结束向量
            t: 插值参数 (0-1)
            
        Returns:
            插值结果向量
        """
        return Vec3(
            ParticleUtils.lerp(a.x, b.x, t),
            ParticleUtils.lerp(a.y, b.y, t),
            ParticleUtils.lerp(a.z, b.z, t)
        )
        
    @staticmethod
    def smooth_step(t: float) -> float:
        """
        平滑步进函数
        
        Args:
            t: 输入值 (0-1)
            
        Returns:
            平滑后的值
        """
        t = max(0.0, min(1.0, t))
        return t * t * (3.0 - 2.0 * t)
        
    @staticmethod
    def ease_in_out(t: float) -> float:
        """
        缓入缓出函数
        
        Args:
            t: 输入值 (0-1)
            
        Returns:
            缓动后的值
        """
        if t < 0.5:
            return 2.0 * t * t
        else:
            return -1.0 + (4.0 - 2.0 * t) * t
            
    @staticmethod
    def random_in_sphere(radius: float = 1.0) -> Vec3:
        """
        在球体内生成随机点
        
        Args:
            radius: 球体半径
            
        Returns:
            随机点坐标
        """
        # 使用拒绝采样法
        while True:
            x = random.uniform(-1, 1)
            y = random.uniform(-1, 1)
            z = random.uniform(-1, 1)
            
            if x*x + y*y + z*z <= 1.0:
                return Vec3(x, y, z) * radius
                
    @staticmethod
    def random_on_sphere(radius: float = 1.0) -> Vec3:
        """
        在球面上生成随机点
        
        Args:
            radius: 球体半径
            
        Returns:
            随机点坐标
        """
        # 使用正态分布方法
        x = random.gauss(0, 1)
        y = random.gauss(0, 1)
        z = random.gauss(0, 1)
        
        length = math.sqrt(x*x + y*y + z*z)
        if length > 0:
            return Vec3(x/length, y/length, z/length) * radius
        else:
            return Vec3(0, 0, radius)
            
    @staticmethod
    def random_in_circle(radius: float = 1.0) -> Vec3:
        """
        在圆形内生成随机点 (Z=0)
        
        Args:
            radius: 圆形半径
            
        Returns:
            随机点坐标
        """
        angle = random.uniform(0, 2 * math.pi)
        r = random.uniform(0, radius)
        return Vec3(r * math.cos(angle), r * math.sin(angle), 0)
        
    @staticmethod
    def random_on_circle(radius: float = 1.0) -> Vec3:
        """
        在圆周上生成随机点 (Z=0)
        
        Args:
            radius: 圆形半径
            
        Returns:
            随机点坐标
        """
        angle = random.uniform(0, 2 * math.pi)
        return Vec3(radius * math.cos(angle), radius * math.sin(angle), 0)
        
    @staticmethod
    def random_in_box(dimensions: Vec3) -> Vec3:
        """
        在盒子内生成随机点
        
        Args:
            dimensions: 盒子尺寸
            
        Returns:
            随机点坐标
        """
        return Vec3(
            random.uniform(-dimensions.x/2, dimensions.x/2),
            random.uniform(-dimensions.y/2, dimensions.y/2),
            random.uniform(-dimensions.z/2, dimensions.z/2)
        )
        
    @staticmethod
    def random_cone_direction(direction: Vec3, cone_angle: float) -> Vec3:
        """
        在锥形范围内生成随机方向
        
        Args:
            direction: 锥形中心方向
            cone_angle: 锥角 (弧度)
            
        Returns:
            随机方向向量
        """
        # 生成锥形内的随机方向
        phi = random.uniform(0, 2 * math.pi)
        cos_theta = random.uniform(math.cos(cone_angle), 1.0)
        sin_theta = math.sqrt(1.0 - cos_theta * cos_theta)
        
        # 局部坐标系中的方向
        local_dir = Vec3(
            sin_theta * math.cos(phi),
            sin_theta * math.sin(phi),
            cos_theta
        )
        
        # 转换到世界坐标系
        # 简化实现：假设direction是(0,0,1)
        if abs(direction.z - 1.0) < 0.001:
            return local_dir
        elif abs(direction.z + 1.0) < 0.001:
            return Vec3(local_dir.x, -local_dir.y, -local_dir.z)
        else:
            # 需要完整的旋转变换
            # 这里使用简化版本
            return local_dir
            
    @staticmethod
    def calculate_billboard_rotation(position: Point3, camera_pos: Point3) -> Quat:
        """
        计算广告牌旋转
        
        Args:
            position: 广告牌位置
            camera_pos: 摄像机位置
            
        Returns:
            旋转四元数
        """
        to_camera = camera_pos - position
        to_camera.normalize()
        
        # 计算旋转四元数
        up = Vec3(0, 0, 1)
        right = to_camera.cross(up)
        
        if right.lengthSquared() < 0.001:
            # 摄像机在正上方或正下方
            right = Vec3(1, 0, 0)
            
        right.normalize()
        up = right.cross(to_camera)
        up.normalize()
        
        # 构建旋转矩阵并转换为四元数
        # 简化实现
        return Quat()
        
    @staticmethod
    def apply_noise(value: float, noise_strength: float, time: float = 0.0) -> float:
        """
        应用噪声到数值
        
        Args:
            value: 原始值
            noise_strength: 噪声强度
            time: 时间参数
            
        Returns:
            添加噪声后的值
        """
        # 简单的伪随机噪声
        noise = math.sin(value * 12.9898 + time * 78.233) * 43758.5453
        noise = noise - math.floor(noise)  # 取小数部分
        noise = (noise - 0.5) * 2.0  # 转换到 -1 到 1
        
        return value + noise * noise_strength
        
    @staticmethod
    def calculate_drag_force(velocity: Vec3, drag_coefficient: float, 
                           air_density: float = 1.225) -> Vec3:
        """
        计算阻力
        
        Args:
            velocity: 速度向量
            drag_coefficient: 阻力系数
            air_density: 空气密度
            
        Returns:
            阻力向量
        """
        speed_squared = velocity.lengthSquared()
        if speed_squared < 0.001:
            return Vec3(0, 0, 0)
            
        speed = math.sqrt(speed_squared)
        drag_magnitude = 0.5 * air_density * drag_coefficient * speed_squared
        
        # 阻力方向与速度相反
        drag_direction = -velocity / speed
        
        return drag_direction * drag_magnitude
        
    @staticmethod
    def calculate_buoyancy_force(volume: float, fluid_density: float = 1000.0,
                                gravity: float = 9.81) -> float:
        """
        计算浮力
        
        Args:
            volume: 物体体积
            fluid_density: 流体密度
            gravity: 重力加速度
            
        Returns:
            浮力大小
        """
        return volume * fluid_density * gravity
        
    @staticmethod
    def distance_squared(a: Point3, b: Point3) -> float:
        """
        计算两点间距离的平方
        
        Args:
            a: 点A
            b: 点B
            
        Returns:
            距离的平方
        """
        dx = a.x - b.x
        dy = a.y - b.y
        dz = a.z - b.z
        return dx*dx + dy*dy + dz*dz
        
    @staticmethod
    def clamp(value: float, min_val: float, max_val: float) -> float:
        """
        限制数值范围
        
        Args:
            value: 输入值
            min_val: 最小值
            max_val: 最大值
            
        Returns:
            限制后的值
        """
        return max(min_val, min(max_val, value))
        
    @staticmethod
    def wrap_angle(angle: float) -> float:
        """
        将角度限制在 -π 到 π 范围内
        
        Args:
            angle: 输入角度 (弧度)
            
        Returns:
            限制后的角度
        """
        while angle > math.pi:
            angle -= 2 * math.pi
        while angle < -math.pi:
            angle += 2 * math.pi
        return angle
        
    @staticmethod
    def create_color_gradient(colors: List[Vec3], positions: List[float], t: float) -> Vec3:
        """
        创建颜色渐变
        
        Args:
            colors: 颜色列表
            positions: 位置列表 (0-1)
            t: 插值参数 (0-1)
            
        Returns:
            插值后的颜色
        """
        if not colors or not positions or len(colors) != len(positions):
            return Vec3(1, 1, 1)
            
        if len(colors) == 1:
            return colors[0]
            
        # 找到插值区间
        for i in range(len(positions) - 1):
            if t <= positions[i + 1]:
                # 在区间 [i, i+1] 内插值
                local_t = (t - positions[i]) / (positions[i + 1] - positions[i])
                return ParticleUtils.lerp_vec3(colors[i], colors[i + 1], local_t)
                
        # 超出范围，返回最后一个颜色
        return colors[-1]
        
    @staticmethod
    def create_size_curve(sizes: List[float], positions: List[float], t: float) -> float:
        """
        创建尺寸曲线
        
        Args:
            sizes: 尺寸列表
            positions: 位置列表 (0-1)
            t: 插值参数 (0-1)
            
        Returns:
            插值后的尺寸
        """
        if not sizes or not positions or len(sizes) != len(positions):
            return 1.0
            
        if len(sizes) == 1:
            return sizes[0]
            
        # 找到插值区间
        for i in range(len(positions) - 1):
            if t <= positions[i + 1]:
                # 在区间 [i, i+1] 内插值
                local_t = (t - positions[i]) / (positions[i + 1] - positions[i])
                return ParticleUtils.lerp(sizes[i], sizes[i + 1], local_t)
                
        # 超出范围，返回最后一个尺寸
        return sizes[-1]
        
    @staticmethod
    def generate_spiral_positions(count: int, radius: float, height: float, 
                                turns: float = 2.0) -> List[Point3]:
        """
        生成螺旋形位置
        
        Args:
            count: 点的数量
            radius: 螺旋半径
            height: 螺旋高度
            turns: 螺旋圈数
            
        Returns:
            位置列表
        """
        positions = []
        
        for i in range(count):
            t = i / max(1, count - 1)
            angle = t * turns * 2 * math.pi
            z = t * height
            
            x = radius * math.cos(angle)
            y = radius * math.sin(angle)
            
            positions.append(Point3(x, y, z))
            
        return positions
        
    @staticmethod
    def calculate_particle_bounds(positions: List[Point3]) -> Tuple[Point3, Point3]:
        """
        计算粒子边界框
        
        Args:
            positions: 粒子位置列表
            
        Returns:
            (最小点, 最大点)
        """
        if not positions:
            return Point3(0, 0, 0), Point3(0, 0, 0)
            
        min_pos = Point3(positions[0])
        max_pos = Point3(positions[0])
        
        for pos in positions[1:]:
            min_pos.x = min(min_pos.x, pos.x)
            min_pos.y = min(min_pos.y, pos.y)
            min_pos.z = min(min_pos.z, pos.z)
            
            max_pos.x = max(max_pos.x, pos.x)
            max_pos.y = max(max_pos.y, pos.y)
            max_pos.z = max(max_pos.z, pos.z)
            
        return min_pos, max_pos