/**
 * GlitchEffect class
 * 
 * Creates cyberpunk-style digital glitch effects
 */
class GlitchEffect {
  float glitchIntensity;
  ArrayList<GlitchLine> glitchLines;
  ArrayList<GlitchBlock> glitchBlocks;
  
  // Timing
  float lastGlitchTime;
  float glitchDuration;
  
  GlitchEffect() {
    glitchIntensity = 0;
    glitchLines = new ArrayList<GlitchLine>();
    glitchBlocks = new ArrayList<GlitchBlock>();
    lastGlitchTime = 0;
    glitchDuration = 0;
  }
  
  void apply() {
    // Natural decay of effect
    glitchIntensity *= 0.95;
    
    // Check if glitch duration is over
    if (millis() - lastGlitchTime > glitchDuration) {
      // Random chance of a new glitch
      if (random(100) < 2) {
        trigger(random(0.1, 0.3));
      }
    }
    
    // Apply glitch effects if active
    if (glitchIntensity > 0.01) {
      // Update and draw glitch lines
      updateGlitchLines();
      drawGlitchLines();
      
      // Update and draw glitch blocks
      updateGlitchBlocks();
      drawGlitchBlocks();
      
      // Optional: Apply color shift
      if (glitchIntensity > 0.2) {
        applyColorShift();
      }
    }
  }
  
  void trigger(float intensity) {
    // Start a new glitch effect
    glitchIntensity = min(glitchIntensity + intensity, 1.0);
    lastGlitchTime = millis();
    glitchDuration = random(100, 500) * intensity;
    
    // Create new glitch elements
    createGlitchLines();
    createGlitchBlocks();
  }
  
  void createGlitchLines() {
    // Clear existing lines
    glitchLines.clear();
    
    // Create new lines
    int numLines = int(5 * glitchIntensity);
    for (int i = 0; i < numLines; i++) {
      glitchLines.add(new GlitchLine());
    }
  }
  
  void updateGlitchLines() {
    // Update all glitch lines
    for (GlitchLine line : glitchLines) {
      line.update();
    }
  }
  
  void drawGlitchLines() {
    // Draw all glitch lines
    for (GlitchLine line : glitchLines) {
      line.display();
    }
  }
  
  void createGlitchBlocks() {
    // Clear existing blocks
    glitchBlocks.clear();
    
    // Create new blocks
    int numBlocks = int(10 * glitchIntensity);
    for (int i = 0; i < numBlocks; i++) {
      glitchBlocks.add(new GlitchBlock());
    }
  }
  
  void updateGlitchBlocks() {
    // Update all glitch blocks
    for (int i = glitchBlocks.size() - 1; i >= 0; i--) {
      GlitchBlock block = glitchBlocks.get(i);
      block.update();
      
      // Remove expired blocks
      if (block.isDead()) {
        glitchBlocks.remove(i);
      }
    }
    
    // Add new blocks randomly
    if (random(100) < 20 * glitchIntensity && glitchBlocks.size() < 20) {
      glitchBlocks.add(new GlitchBlock());
    }
  }
  
  void drawGlitchBlocks() {
    // Draw all glitch blocks
    for (GlitchBlock block : glitchBlocks) {
      block.display();
    }
  }
  
  void applyColorShift() {
    // Create RGB shift effect
    blendMode(EXCLUSION);
    noStroke();
    
    // Red shift
    fill(255, 0, 0, 40 * glitchIntensity);
    rect(random(-5, 5), random(-5, 5), width, height);
    
    // Blue shift
    fill(0, 0, 255, 40 * glitchIntensity);
    rect(random(-5, 5), random(-5, 5), width, height);
    
    blendMode(BLEND);
  }
}

/**
 * GlitchLine class
 * 
 * Horizontal line that creates a scan-line glitch effect
 */
class GlitchLine {
  float yPosition;
  float height;
  float offset;
  float speed;
  color lineColor;
  float alpha;
  
  GlitchLine() {
    yPosition = random(height);
    this.height = random(2, 10);
    offset = random(-30, 30);
    speed = random(-2, 2);
    
    // Random RGB channel emphasis
    int channel = int(random(3));
    switch(channel) {
      case 0:
        lineColor = color(255, 50, 50); // Red
        break;
      case 1:
        lineColor = color(50, 255, 50); // Green
        break;
      case 2:
        lineColor = color(50, 50, 255); // Blue
        break;
    }
    
    alpha = random(100, 200);
  }
  
  void update() {
    // Move the line
    yPosition += speed;
    
    // Wrap around screen
    if (yPosition < 0) yPosition = height;
    if (yPosition > height) yPosition = 0;
    
    // Randomize offset occasionally
    if (random(100) < 5) {
      offset = random(-30, 30);
    }
  }
  
  void display() {
    // Draw the line
    noStroke();
    fill(red(lineColor), green(lineColor), blue(lineColor), alpha);
    rect(0, yPosition, width, this.height);
    
    // Draw offset segment
    int segmentWidth = int(random(50, width/2));
    int segmentX = int(random(width - segmentWidth));
    
    // Draw offset segment
    rect(segmentX, yPosition + offset, segmentWidth, this.height);
  }
}

/**
 * GlitchBlock class
 * 
 * Rectangular block that creates digital artifact effects
 */
class GlitchBlock {
  PVector position;
  float blockWidth;
  float blockHeight;
  color blockColor;
  float alpha;
  float lifespan;
  
  GlitchBlock() {
    position = new PVector(random(width), random(height));
    blockWidth = random(20, 100);
    blockHeight = random(5, 30);
    
    // Use a bright cyberpunk color
    float hue = random(360);
    colorMode(HSB, 360, 100, 100);
    blockColor = color(hue, 80, 100);
    colorMode(RGB, 255, 255, 255);
    
    alpha = random(50, 150);
    lifespan = random(10, 30);
  }
  
  void update() {
    // Decrease lifespan
    lifespan--;
    
    // Random position changes
    if (random(100) < 30) {
      position.x = random(width);
    }
  }
  
  void display() {
    // Draw the block
    noStroke();
    fill(red(blockColor), green(blockColor), blue(blockColor), alpha);
    rect(position.x, position.y, blockWidth, blockHeight);
    
    // Draw some noise inside
    fill(255, alpha * 0.7);
    for (int i = 0; i < 5; i++) {
      float noiseX = position.x + random(blockWidth);
      float noiseY = position.y + random(blockHeight);
      float noiseSize = random(2, 5);
      rect(noiseX, noiseY, noiseSize, noiseSize);
    }
  }
  
  boolean isDead() {
    return lifespan <= 0;
  }
}
