/**
 * ParticleSystem class
 * 
 * Creates and manages particles that respond to musical events
 */
class ParticleSystem {
  ArrayList<Particle> particles;
  int maxParticles = 500;
  
  ParticleSystem() {
    particles = new ArrayList<Particle>();
  }
  
  void update() {
    // Update all particles
    for (int i = particles.size() - 1; i >= 0; i--) {
      Particle p = particles.get(i);
      p.update();
      
      // Remove dead particles
      if (p.isDead()) {
        particles.remove(i);
      }
    }
  }
  
  void display() {
    // Display all particles
    for (Particle p : particles) {
      p.display();
    }
    
    // Apply blending for glow effect
    blendMode(ADD);
    for (Particle p : particles) {
      if (p.energy > 0.5) {
        p.displayGlow();
      }
    }
    blendMode(BLEND);
  }
  
  void addParticles(int orbit, float pan, float gain) {
    // Skip if at max capacity
    if (particles.size() >= maxParticles) return;
    
    // Number of particles based on gain
    int count = int(5 + (gain * 20));
    
    // Position based on orbit and pan
    float x = map(pan, 0, 1, width * 0.3, width * 0.7);
    float y = map(orbit, 0, 15, height * 0.2, height * 0.8);
    
    // Create particles
    for (int i = 0; i < count; i++) {
      if (particles.size() < maxParticles) {
        particles.add(new Particle(x, y, orbitColors[orbit], gain));
      }
    }
  }
  
  int getParticleCount() {
    return particles.size();
  }
  
  void reset() {
    particles.clear();
  }
}

/**
 * Particle class
 * 
 * Individual particle with physics for visual effects
 */
class Particle {
  PVector position;
  PVector velocity;
  PVector acceleration;
  
  color particleColor;
  float size;
  float lifespan;
  float maxLife;
  float energy;
  
  Particle(float x, float y, color c, float gain) {
    position = new PVector(x, y);
    
    // Random velocity
    float angle = random(TWO_PI);
    float speed = random(1, 3 + (gain * 5));
    velocity = new PVector(cos(angle) * speed, sin(angle) * speed);
    
    // Slight downward acceleration (gravity)
    acceleration = new PVector(0, 0.05);
    
    // Visual properties
    particleColor = c;
    size = random(2, 8);
    maxLife = random(500, 2000);
    lifespan = maxLife;
    energy = gain;
  }
  
  void update() {
    // Apply physics
    velocity.add(acceleration);
    position.add(velocity);
    
    // Add some random movement
    velocity.x += random(-0.1, 0.1);
    velocity.y += random(-0.1, 0.1);
    
    // Slow down over time
    velocity.mult(0.98);
    
    // Decrease lifespan
    lifespan -= 10;
    
    // Bounce off edges with energy loss
    if (position.x < 0 || position.x > width) {
      velocity.x *= -0.8;
      position.x = constrain(position.x, 0, width);
    }
    
    if (position.y < 0 || position.y > height) {
      velocity.y *= -0.8;
      position.y = constrain(position.y, 0, height);
    }
  }
  
  void display() {
    // Calculate alpha based on remaining life
    float alpha = map(lifespan, 0, maxLife, 0, 200);
    
    // Draw particle
    noStroke();
    fill(red(particleColor), green(particleColor), blue(particleColor), alpha);
    ellipse(position.x, position.y, size, size);
  }
  
  void displayGlow() {
    // Draw glow effect
    float glowSize = size * 3;
    float alpha = map(lifespan, 0, maxLife, 0, 50) * energy;
    
    noStroke();
    fill(red(particleColor), green(particleColor), blue(particleColor), alpha);
    ellipse(position.x, position.y, glowSize, glowSize);
  }
  
  boolean isDead() {
    return lifespan <= 0;
  }
}
