//! Particle system component definitions.

use nalgebra_glm::{Vec3, Vec4};

/// Effect type for categorizing particle behaviors.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum EmitterType {
    /// Burst-style pyrotechnic effects.
    Firework,
    /// Continuous flame-like effects with upward drift.
    Fire,
    /// Slow, expanding, non-emissive particles.
    Smoke,
    /// Fast, small, bright particles with strong gravity.
    Sparks,
    /// Continuous stream following a moving emitter.
    Trail,
}

/// Spatial volume from which particles spawn.
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum EmitterShape {
    /// Single point emission.
    #[default]
    Point,
    /// Emit from random points within a sphere.
    Sphere { radius: f32 },
    /// Emit within a cone pointed along the emitter direction.
    Cone { angle: f32, height: f32 },
    /// Emit from random points within a box.
    Box { half_extents: Vec3 },
}

/// Color and alpha values sampled over particle lifetime.
///
/// Colors are interpolated linearly between keyframes. The time value
/// ranges from 0.0 (particle birth) to 1.0 (particle death).
#[derive(Debug, Clone)]
pub struct ColorGradient {
    /// Keyframes as (normalized_time, rgba_color) pairs.
    pub colors: Vec<(f32, Vec4)>,
}

impl Default for ColorGradient {
    fn default() -> Self {
        Self {
            colors: vec![
                (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
                (1.0, Vec4::new(1.0, 1.0, 1.0, 0.0)),
            ],
        }
    }
}

impl ColorGradient {
    /// Hot to cool gradient for firework trails.
    pub fn firework_trail() -> Self {
        Self {
            colors: vec![
                (0.0, Vec4::new(1.0, 1.0, 0.95, 1.0)),
                (0.15, Vec4::new(1.0, 0.95, 0.7, 1.0)),
                (0.4, Vec4::new(1.0, 0.7, 0.3, 0.85)),
                (0.7, Vec4::new(1.0, 0.4, 0.1, 0.5)),
                (1.0, Vec4::new(0.6, 0.15, 0.0, 0.0)),
            ],
        }
    }

    /// Standard firework burst gradient with custom base color.
    pub fn firework_explosion(base_color: Vec3) -> Self {
        Self {
            colors: vec![
                (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
                (
                    0.05,
                    Vec4::new(
                        (base_color.x + 1.0) * 0.5,
                        (base_color.y + 1.0) * 0.5,
                        (base_color.z + 1.0) * 0.5,
                        1.0,
                    ),
                ),
                (
                    0.15,
                    Vec4::new(base_color.x, base_color.y, base_color.z, 1.0),
                ),
                (
                    0.4,
                    Vec4::new(base_color.x, base_color.y, base_color.z, 0.95),
                ),
                (
                    0.65,
                    Vec4::new(
                        base_color.x * 0.85,
                        base_color.y * 0.85,
                        base_color.z * 0.85,
                        0.7,
                    ),
                ),
                (
                    0.85,
                    Vec4::new(
                        base_color.x * 0.5,
                        base_color.y * 0.5,
                        base_color.z * 0.5,
                        0.3,
                    ),
                ),
                (1.0, Vec4::new(0.1, 0.1, 0.1, 0.0)),
            ],
        }
    }

    /// Long-lived willow-style drooping effect.
    pub fn firework_willow(base_color: Vec3) -> Self {
        Self {
            colors: vec![
                (0.0, Vec4::new(1.0, 1.0, 0.95, 1.0)),
                (
                    0.1,
                    Vec4::new(base_color.x, base_color.y, base_color.z, 1.0),
                ),
                (
                    0.3,
                    Vec4::new(base_color.x, base_color.y, base_color.z, 0.9),
                ),
                (
                    0.6,
                    Vec4::new(
                        base_color.x * 0.7,
                        base_color.y * 0.8,
                        base_color.z * 0.6,
                        0.6,
                    ),
                ),
                (
                    0.85,
                    Vec4::new(
                        base_color.x * 0.4,
                        base_color.y * 0.5,
                        base_color.z * 0.3,
                        0.25,
                    ),
                ),
                (1.0, Vec4::new(0.1, 0.1, 0.05, 0.0)),
            ],
        }
    }

    pub fn firework_glitter() -> Self {
        Self {
            colors: vec![
                (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
                (0.2, Vec4::new(1.0, 1.0, 0.9, 1.0)),
                (0.5, Vec4::new(1.0, 0.95, 0.7, 0.9)),
                (0.8, Vec4::new(1.0, 0.85, 0.5, 0.5)),
                (1.0, Vec4::new(0.8, 0.6, 0.2, 0.0)),
            ],
        }
    }

    pub fn firework_crackle() -> Self {
        Self {
            colors: vec![
                (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
                (0.1, Vec4::new(1.0, 0.95, 0.8, 1.0)),
                (0.3, Vec4::new(1.0, 0.8, 0.4, 0.9)),
                (0.6, Vec4::new(1.0, 0.5, 0.1, 0.6)),
                (1.0, Vec4::new(0.4, 0.1, 0.0, 0.0)),
            ],
        }
    }

    pub fn flash_burst() -> Self {
        Self {
            colors: vec![
                (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
                (0.15, Vec4::new(1.0, 1.0, 0.95, 1.0)),
                (0.4, Vec4::new(1.0, 0.95, 0.8, 0.7)),
                (0.7, Vec4::new(1.0, 0.9, 0.6, 0.3)),
                (1.0, Vec4::new(1.0, 0.8, 0.4, 0.0)),
            ],
        }
    }

    pub fn comet_trail() -> Self {
        Self {
            colors: vec![
                (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
                (0.1, Vec4::new(1.0, 0.98, 0.9, 1.0)),
                (0.25, Vec4::new(1.0, 0.9, 0.6, 0.95)),
                (0.5, Vec4::new(1.0, 0.7, 0.3, 0.8)),
                (0.75, Vec4::new(1.0, 0.4, 0.1, 0.5)),
                (1.0, Vec4::new(0.5, 0.1, 0.0, 0.0)),
            ],
        }
    }

    pub fn palm_frond(base_color: Vec3) -> Self {
        Self {
            colors: vec![
                (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
                (
                    0.08,
                    Vec4::new(base_color.x, base_color.y, base_color.z, 1.0),
                ),
                (
                    0.25,
                    Vec4::new(base_color.x, base_color.y, base_color.z, 0.95),
                ),
                (
                    0.5,
                    Vec4::new(
                        base_color.x * 0.9,
                        base_color.y * 0.9,
                        base_color.z * 0.9,
                        0.85,
                    ),
                ),
                (
                    0.75,
                    Vec4::new(
                        base_color.x * 0.7,
                        base_color.y * 0.7,
                        base_color.z * 0.7,
                        0.5,
                    ),
                ),
                (
                    1.0,
                    Vec4::new(
                        base_color.x * 0.3,
                        base_color.y * 0.3,
                        base_color.z * 0.3,
                        0.0,
                    ),
                ),
            ],
        }
    }

    pub fn crossette() -> Self {
        Self {
            colors: vec![
                (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
                (0.1, Vec4::new(1.0, 0.95, 0.85, 1.0)),
                (0.3, Vec4::new(1.0, 0.85, 0.6, 0.9)),
                (0.6, Vec4::new(1.0, 0.6, 0.2, 0.7)),
                (1.0, Vec4::new(0.6, 0.2, 0.0, 0.0)),
            ],
        }
    }

    pub fn strobe() -> Self {
        Self {
            colors: vec![
                (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
                (0.5, Vec4::new(1.0, 1.0, 1.0, 1.0)),
                (1.0, Vec4::new(0.8, 0.8, 0.8, 0.0)),
            ],
        }
    }

    pub fn fire() -> Self {
        Self {
            colors: vec![
                (0.0, Vec4::new(1.0, 1.0, 0.9, 1.0)),
                (0.2, Vec4::new(1.0, 0.9, 0.4, 1.0)),
                (0.4, Vec4::new(1.0, 0.5, 0.1, 0.9)),
                (0.7, Vec4::new(0.9, 0.2, 0.0, 0.6)),
                (1.0, Vec4::new(0.3, 0.05, 0.0, 0.0)),
            ],
        }
    }

    pub fn smoke() -> Self {
        Self {
            colors: vec![
                (0.0, Vec4::new(0.4, 0.4, 0.45, 0.0)),
                (0.1, Vec4::new(0.5, 0.5, 0.55, 0.6)),
                (0.4, Vec4::new(0.55, 0.55, 0.6, 0.5)),
                (0.7, Vec4::new(0.6, 0.6, 0.65, 0.3)),
                (1.0, Vec4::new(0.65, 0.65, 0.7, 0.0)),
            ],
        }
    }

    pub fn sparks() -> Self {
        Self {
            colors: vec![
                (0.0, Vec4::new(1.0, 1.0, 0.8, 1.0)),
                (0.3, Vec4::new(1.0, 0.8, 0.2, 1.0)),
                (0.7, Vec4::new(1.0, 0.4, 0.0, 0.7)),
                (1.0, Vec4::new(0.5, 0.1, 0.0, 0.0)),
            ],
        }
    }

    /// Converts to GPU-compatible format (max 8 keyframes).
    pub fn to_gpu_data(&self) -> [[f32; 8]; 8] {
        let mut data = [[0.0f32; 8]; 8];
        for (index, (time, color)) in self.colors.iter().take(8).enumerate() {
            data[index] = [*time, color.x, color.y, color.z, color.w, 0.0, 0.0, 0.0];
        }
        data
    }

    /// Returns the number of keyframes (capped at 8 for GPU).
    pub fn color_count(&self) -> u32 {
        self.colors.len().min(8) as u32
    }
}

/// Particle emitter component.
///
/// Spawns particles continuously at `spawn_rate` or in bursts of `burst_count`.
/// Each particle is simulated independently with its own velocity, gravity, and lifetime.
#[derive(Debug, Clone)]
pub struct ParticleEmitter {
    /// Effect type for shader selection and behavior hints.
    pub emitter_type: EmitterType,
    /// Volume from which particles are emitted.
    pub shape: EmitterShape,
    /// Emitter position in local space (added to entity transform).
    pub position: Vec3,
    /// Primary emission direction (for directional shapes).
    pub direction: Vec3,
    /// Particles spawned per second (0 for burst-only).
    pub spawn_rate: f32,
    /// Number of particles to spawn immediately when enabled.
    pub burst_count: u32,
    /// Minimum particle lifetime in seconds.
    pub particle_lifetime_min: f32,
    /// Maximum particle lifetime in seconds.
    pub particle_lifetime_max: f32,
    /// Minimum initial speed along emission direction.
    pub initial_velocity_min: f32,
    /// Maximum initial speed along emission direction.
    pub initial_velocity_max: f32,
    /// Random angular spread from direction (radians).
    pub velocity_spread: f32,
    /// Acceleration applied each frame (typically downward for gravity).
    pub gravity: Vec3,
    /// Velocity damping factor (0 = no damping, 1 = full stop immediately).
    pub drag: f32,
    /// Particle size at birth.
    pub size_start: f32,
    /// Particle size at death.
    pub size_end: f32,
    /// Color and alpha over particle lifetime.
    pub color_gradient: ColorGradient,
    /// Emission multiplier for HDR bloom.
    pub emissive_strength: f32,
    /// Whether the emitter is active.
    pub enabled: bool,
    /// Internal accumulator for fractional particle spawning.
    pub accumulated_spawn: f32,
    /// If true, fires burst once then disables.
    pub one_shot: bool,
    /// Whether the one-shot burst has already fired.
    pub has_fired: bool,
    /// Strength of noise-based position perturbation.
    pub turbulence_strength: f32,
    /// Frequency of turbulence noise sampling.
    pub turbulence_frequency: f32,
}

impl Default for ParticleEmitter {
    fn default() -> Self {
        Self {
            emitter_type: EmitterType::Fire,
            shape: EmitterShape::Point,
            position: Vec3::zeros(),
            direction: Vec3::new(0.0, 1.0, 0.0),
            spawn_rate: 100.0,
            burst_count: 0,
            particle_lifetime_min: 1.0,
            particle_lifetime_max: 2.0,
            initial_velocity_min: 1.0,
            initial_velocity_max: 3.0,
            velocity_spread: 0.3,
            gravity: Vec3::new(0.0, -9.81, 0.0),
            drag: 0.1,
            size_start: 0.1,
            size_end: 0.0,
            color_gradient: ColorGradient::default(),
            emissive_strength: 1.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: false,
            has_fired: false,
            turbulence_strength: 0.0,
            turbulence_frequency: 1.0,
        }
    }
}

impl ParticleEmitter {
    /// Creates a rising firework shell with trailing sparks.
    pub fn firework_shell(position: Vec3, velocity: Vec3) -> Self {
        Self {
            emitter_type: EmitterType::Firework,
            shape: EmitterShape::Point,
            position,
            direction: velocity.normalize(),
            spawn_rate: 120.0,
            burst_count: 0,
            particle_lifetime_min: 0.15,
            particle_lifetime_max: 0.4,
            initial_velocity_min: velocity.magnitude() * 0.05,
            initial_velocity_max: velocity.magnitude() * 0.15,
            velocity_spread: 0.25,
            gravity: Vec3::new(0.0, -4.0, 0.0),
            drag: 0.1,
            size_start: 0.15,
            size_end: 0.03,
            color_gradient: ColorGradient::firework_trail(),
            emissive_strength: 5.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: false,
            has_fired: false,
            turbulence_strength: 0.2,
            turbulence_frequency: 2.0,
        }
    }

    /// Creates a spherical firework burst.
    pub fn firework_explosion(position: Vec3, color: Vec3, particle_count: u32) -> Self {
        Self {
            emitter_type: EmitterType::Firework,
            shape: EmitterShape::Sphere { radius: 0.5 },
            position,
            direction: Vec3::new(0.0, 1.0, 0.0),
            spawn_rate: 0.0,
            burst_count: particle_count,
            particle_lifetime_min: 2.0,
            particle_lifetime_max: 3.5,
            initial_velocity_min: 12.0,
            initial_velocity_max: 25.0,
            velocity_spread: std::f32::consts::PI,
            gravity: Vec3::new(0.0, -4.0, 0.0),
            drag: 0.4,
            size_start: 0.25,
            size_end: 0.04,
            color_gradient: ColorGradient::firework_explosion(color),
            emissive_strength: 8.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: true,
            has_fired: false,
            turbulence_strength: 0.3,
            turbulence_frequency: 1.5,
        }
    }

    pub fn firework_willow(position: Vec3, color: Vec3, particle_count: u32) -> Self {
        Self {
            emitter_type: EmitterType::Firework,
            shape: EmitterShape::Sphere { radius: 0.3 },
            position,
            direction: Vec3::new(0.0, 1.0, 0.0),
            spawn_rate: 0.0,
            burst_count: particle_count,
            particle_lifetime_min: 3.5,
            particle_lifetime_max: 5.5,
            initial_velocity_min: 8.0,
            initial_velocity_max: 18.0,
            velocity_spread: std::f32::consts::PI,
            gravity: Vec3::new(0.0, -6.0, 0.0),
            drag: 0.15,
            size_start: 0.18,
            size_end: 0.02,
            color_gradient: ColorGradient::firework_willow(color),
            emissive_strength: 6.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: true,
            has_fired: false,
            turbulence_strength: 0.1,
            turbulence_frequency: 0.8,
        }
    }

    pub fn firework_chrysanthemum(position: Vec3, color: Vec3, particle_count: u32) -> Self {
        Self {
            emitter_type: EmitterType::Firework,
            shape: EmitterShape::Sphere { radius: 0.2 },
            position,
            direction: Vec3::new(0.0, 1.0, 0.0),
            spawn_rate: 0.0,
            burst_count: particle_count,
            particle_lifetime_min: 2.5,
            particle_lifetime_max: 4.0,
            initial_velocity_min: 15.0,
            initial_velocity_max: 30.0,
            velocity_spread: std::f32::consts::PI,
            gravity: Vec3::new(0.0, -2.5, 0.0),
            drag: 0.6,
            size_start: 0.2,
            size_end: 0.05,
            color_gradient: ColorGradient::firework_explosion(color),
            emissive_strength: 10.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: true,
            has_fired: false,
            turbulence_strength: 0.15,
            turbulence_frequency: 1.2,
        }
    }

    pub fn firework_ring(position: Vec3, color: Vec3, particle_count: u32) -> Self {
        Self {
            emitter_type: EmitterType::Firework,
            shape: EmitterShape::Point,
            position,
            direction: Vec3::new(0.0, 0.0, 1.0),
            spawn_rate: 0.0,
            burst_count: particle_count,
            particle_lifetime_min: 2.0,
            particle_lifetime_max: 3.0,
            initial_velocity_min: 18.0,
            initial_velocity_max: 22.0,
            velocity_spread: 0.15,
            gravity: Vec3::new(0.0, -1.5, 0.0),
            drag: 0.35,
            size_start: 0.22,
            size_end: 0.03,
            color_gradient: ColorGradient::firework_explosion(color),
            emissive_strength: 9.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: true,
            has_fired: false,
            turbulence_strength: 0.05,
            turbulence_frequency: 1.0,
        }
    }

    pub fn firework_glitter(position: Vec3, particle_count: u32) -> Self {
        Self {
            emitter_type: EmitterType::Sparks,
            shape: EmitterShape::Sphere { radius: 2.0 },
            position,
            direction: Vec3::new(0.0, 1.0, 0.0),
            spawn_rate: 0.0,
            burst_count: particle_count,
            particle_lifetime_min: 0.8,
            particle_lifetime_max: 2.5,
            initial_velocity_min: 1.0,
            initial_velocity_max: 5.0,
            velocity_spread: std::f32::consts::PI,
            gravity: Vec3::new(0.0, -8.0, 0.0),
            drag: 0.05,
            size_start: 0.12,
            size_end: 0.02,
            color_gradient: ColorGradient::firework_glitter(),
            emissive_strength: 12.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: true,
            has_fired: false,
            turbulence_strength: 0.8,
            turbulence_frequency: 4.0,
        }
    }

    pub fn firework_crackle(position: Vec3, particle_count: u32) -> Self {
        Self {
            emitter_type: EmitterType::Sparks,
            shape: EmitterShape::Sphere { radius: 1.5 },
            position,
            direction: Vec3::new(0.0, 1.0, 0.0),
            spawn_rate: 0.0,
            burst_count: particle_count,
            particle_lifetime_min: 0.3,
            particle_lifetime_max: 0.8,
            initial_velocity_min: 3.0,
            initial_velocity_max: 10.0,
            velocity_spread: std::f32::consts::PI,
            gravity: Vec3::new(0.0, -12.0, 0.0),
            drag: 0.02,
            size_start: 0.08,
            size_end: 0.01,
            color_gradient: ColorGradient::firework_crackle(),
            emissive_strength: 15.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: true,
            has_fired: false,
            turbulence_strength: 1.2,
            turbulence_frequency: 6.0,
        }
    }

    pub fn flash_burst(position: Vec3) -> Self {
        Self {
            emitter_type: EmitterType::Firework,
            shape: EmitterShape::Point,
            position,
            direction: Vec3::new(0.0, 1.0, 0.0),
            spawn_rate: 0.0,
            burst_count: 50,
            particle_lifetime_min: 0.1,
            particle_lifetime_max: 0.25,
            initial_velocity_min: 2.0,
            initial_velocity_max: 8.0,
            velocity_spread: std::f32::consts::PI,
            gravity: Vec3::new(0.0, 0.0, 0.0),
            drag: 0.0,
            size_start: 1.5,
            size_end: 0.3,
            color_gradient: ColorGradient::flash_burst(),
            emissive_strength: 25.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: true,
            has_fired: false,
            turbulence_strength: 0.0,
            turbulence_frequency: 0.0,
        }
    }

    pub fn comet_shell(position: Vec3, velocity: Vec3) -> Self {
        Self {
            emitter_type: EmitterType::Firework,
            shape: EmitterShape::Point,
            position,
            direction: velocity.normalize(),
            spawn_rate: 200.0,
            burst_count: 0,
            particle_lifetime_min: 0.2,
            particle_lifetime_max: 0.5,
            initial_velocity_min: velocity.magnitude() * 0.02,
            initial_velocity_max: velocity.magnitude() * 0.08,
            velocity_spread: 0.15,
            gravity: Vec3::new(0.0, -2.0, 0.0),
            drag: 0.05,
            size_start: 0.25,
            size_end: 0.04,
            color_gradient: ColorGradient::comet_trail(),
            emissive_strength: 8.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: false,
            has_fired: false,
            turbulence_strength: 0.1,
            turbulence_frequency: 1.5,
        }
    }

    pub fn palm_explosion(position: Vec3, color: Vec3, particle_count: u32) -> Self {
        Self {
            emitter_type: EmitterType::Firework,
            shape: EmitterShape::Sphere { radius: 0.2 },
            position,
            direction: Vec3::new(0.0, 1.0, 0.0),
            spawn_rate: 0.0,
            burst_count: particle_count,
            particle_lifetime_min: 3.0,
            particle_lifetime_max: 5.0,
            initial_velocity_min: 20.0,
            initial_velocity_max: 35.0,
            velocity_spread: 0.4,
            gravity: Vec3::new(0.0, -8.0, 0.0),
            drag: 0.25,
            size_start: 0.22,
            size_end: 0.03,
            color_gradient: ColorGradient::palm_frond(color),
            emissive_strength: 8.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: true,
            has_fired: false,
            turbulence_strength: 0.2,
            turbulence_frequency: 1.0,
        }
    }

    pub fn crossette_burst(position: Vec3, color: Vec3, particle_count: u32) -> Self {
        Self {
            emitter_type: EmitterType::Sparks,
            shape: EmitterShape::Sphere { radius: 0.1 },
            position,
            direction: Vec3::new(0.0, 1.0, 0.0),
            spawn_rate: 0.0,
            burst_count: particle_count,
            particle_lifetime_min: 0.5,
            particle_lifetime_max: 1.2,
            initial_velocity_min: 15.0,
            initial_velocity_max: 25.0,
            velocity_spread: std::f32::consts::PI,
            gravity: Vec3::new(0.0, -5.0, 0.0),
            drag: 0.3,
            size_start: 0.15,
            size_end: 0.02,
            color_gradient: ColorGradient::firework_explosion(color),
            emissive_strength: 12.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: true,
            has_fired: false,
            turbulence_strength: 0.5,
            turbulence_frequency: 3.0,
        }
    }

    pub fn strobe_effect(position: Vec3, particle_count: u32) -> Self {
        Self {
            emitter_type: EmitterType::Sparks,
            shape: EmitterShape::Sphere { radius: 3.0 },
            position,
            direction: Vec3::new(0.0, -1.0, 0.0),
            spawn_rate: 0.0,
            burst_count: particle_count,
            particle_lifetime_min: 1.5,
            particle_lifetime_max: 3.0,
            initial_velocity_min: 0.5,
            initial_velocity_max: 2.0,
            velocity_spread: std::f32::consts::PI,
            gravity: Vec3::new(0.0, -6.0, 0.0),
            drag: 0.1,
            size_start: 0.1,
            size_end: 0.02,
            color_gradient: ColorGradient::strobe(),
            emissive_strength: 20.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: true,
            has_fired: false,
            turbulence_strength: 0.3,
            turbulence_frequency: 2.0,
        }
    }

    pub fn trailing_sparks(position: Vec3, velocity: Vec3) -> Self {
        Self {
            emitter_type: EmitterType::Sparks,
            shape: EmitterShape::Point,
            position,
            direction: (-velocity).normalize(),
            spawn_rate: 80.0,
            burst_count: 0,
            particle_lifetime_min: 0.2,
            particle_lifetime_max: 0.6,
            initial_velocity_min: 1.0,
            initial_velocity_max: 4.0,
            velocity_spread: 0.5,
            gravity: Vec3::new(0.0, -15.0, 0.0),
            drag: 0.05,
            size_start: 0.06,
            size_end: 0.01,
            color_gradient: ColorGradient::firework_glitter(),
            emissive_strength: 10.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: false,
            has_fired: false,
            turbulence_strength: 0.8,
            turbulence_frequency: 5.0,
        }
    }

    /// Creates a continuous fire effect.
    pub fn fire(position: Vec3) -> Self {
        Self {
            emitter_type: EmitterType::Fire,
            shape: EmitterShape::Cone {
                angle: 0.3,
                height: 0.1,
            },
            position,
            direction: Vec3::new(0.0, 1.0, 0.0),
            spawn_rate: 200.0,
            burst_count: 0,
            particle_lifetime_min: 0.5,
            particle_lifetime_max: 1.5,
            initial_velocity_min: 2.0,
            initial_velocity_max: 5.0,
            velocity_spread: 0.4,
            gravity: Vec3::new(0.0, 2.0, 0.0),
            drag: 0.5,
            size_start: 0.3,
            size_end: 0.1,
            color_gradient: ColorGradient::fire(),
            emissive_strength: 4.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: false,
            has_fired: false,
            turbulence_strength: 1.5,
            turbulence_frequency: 3.0,
        }
    }

    /// Creates a continuous smoke plume.
    pub fn smoke(position: Vec3) -> Self {
        Self {
            emitter_type: EmitterType::Smoke,
            shape: EmitterShape::Cone {
                angle: 0.1,
                height: 0.2,
            },
            position,
            direction: Vec3::new(0.0, 1.0, 0.0),
            spawn_rate: 50.0,
            burst_count: 0,
            particle_lifetime_min: 2.5,
            particle_lifetime_max: 4.5,
            initial_velocity_min: 0.8,
            initial_velocity_max: 1.8,
            velocity_spread: 0.15,
            gravity: Vec3::new(0.0, 0.6, 0.0),
            drag: 0.3,
            size_start: 0.4,
            size_end: 1.2,
            color_gradient: ColorGradient::smoke(),
            emissive_strength: 0.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: false,
            has_fired: false,
            turbulence_strength: 0.3,
            turbulence_frequency: 0.4,
        }
    }

    /// Creates a continuous spark fountain.
    pub fn sparks(position: Vec3) -> Self {
        Self {
            emitter_type: EmitterType::Sparks,
            shape: EmitterShape::Point,
            position,
            direction: Vec3::new(0.0, 1.0, 0.0),
            spawn_rate: 50.0,
            burst_count: 0,
            particle_lifetime_min: 0.3,
            particle_lifetime_max: 0.8,
            initial_velocity_min: 5.0,
            initial_velocity_max: 10.0,
            velocity_spread: 0.8,
            gravity: Vec3::new(0.0, -15.0, 0.0),
            drag: 0.1,
            size_start: 0.05,
            size_end: 0.01,
            color_gradient: ColorGradient::sparks(),
            emissive_strength: 6.0,
            enabled: true,
            accumulated_spawn: 0.0,
            one_shot: false,
            has_fired: false,
            turbulence_strength: 0.0,
            turbulence_frequency: 1.0,
        }
    }
}

/// Runtime statistics for the particle system.
#[derive(Debug, Clone, Copy, Default)]
pub struct ParticleSystemStats {
    /// Currently alive particles across all emitters.
    pub active_particles: u32,
    /// Total particles spawned since startup.
    pub total_spawned: u64,
    /// Total particles that have reached end of life.
    pub total_died: u64,
}
