import processing.core.*; 
import processing.xml.*; 

import java.applet.*; 
import java.awt.Dimension; 
import java.awt.Frame; 
import java.awt.event.MouseEvent; 
import java.awt.event.KeyEvent; 
import java.awt.event.FocusEvent; 
import java.awt.Image; 
import java.io.*; 
import java.net.*; 
import java.text.*; 
import java.util.*; 
import java.util.zip.*; 
import java.util.regex.*; 

public class sketch_sep04b extends PApplet {

final int NUM_EXPLOSIONS = 40;
Explosion[] fireworks = new Explosion[NUM_EXPLOSIONS];


public void setup()
{
  size(800,600,P3D);
  noStroke();
  
  for ( int i=0; i < NUM_EXPLOSIONS; i++ )
    fireworks[i] = new Explosion();
  
}

public void draw()
{
  background(0);
  for ( int i=0; i < NUM_EXPLOSIONS; i++ )
  {
    Explosion ex = fireworks[i];
    ex.Draw();
    ex.Grow();
  }
  
}

class Coord
{
  private final float MAX_Z = 100;
  
  public float x,y,z;
  public Coord()
  {
    x = random(0,width);
    y = random(0,height);
    z = random(0,MAX_Z);  
  }
  public Coord(float _x, float _y, float _z)
  {
    x = _x;
    y = _y;
    z = _z;
  }
}

class Vertex extends Coord
{
  private Vector myVector;
  public Vertex()
  {
    super();
    InitVector();    
  }
  public Vertex(float _x, float _y, float _z)
  {
    super(_x,_y,_z);
    InitVector();
  }
  
  private void InitVector()
  {
    float vx, vy, vz;
    vx = random(-3, 3);
    vy = random(-3, 3);
    vz = random(-3, 3);
    myVector = new Vector(vx, vy, vz);
  }
  
  public void Move()
  {
    x += myVector.x;
    y += myVector.y;
    z += myVector.z;
    x %= width;
    y %= height;
    z %= super.MAX_Z;
  }
}

class Vector extends Coord
{
  public Vector()
  {
    super();
  }
  public Vector(float _x, float _y, float _z)
  {
    super(_x,_y,_z);
  }
}

class Colour
{
  public int r,g,b,a;
  public Colour()
  {
    r = (int)random(32,255);
    g = (int)random(32,255);
    b = (int)random(32,255);
    a = 255;
  }
  public Colour(int _r, int _g, int _b, int _a)
  {
    r = _r;
    g = _g;
    b = _b;
    a = _a;
  }
  public Colour GetDarkened(int amount)
  {
    int newR,newG,newB,newA;
    newR = max(0,r - amount);
    newG = max(0,g - amount);
    newB = max(0,b - amount);
    newA = max(0,a - amount);
    
    return new Colour(newR, newG, newB, newA);
  }
}

class FCircle
{
  private float numRings = 50f;
  private final float RING_INCREASE_AMOUT = 0.1f;
  private Vertex myPoint;
  private Colour myColor;
  private final int MAX_RADIUS = 20;
  private PGraphics myFrame = null;
  
  public FCircle()
  {    
    myPoint = new Vertex();
    myColor = new Colour();
  }
  public FCircle(Vertex _point, Colour _color)
  {
    myColor = _color;
    myPoint = _point;
  }
  public FCircle(Vertex _point, Colour _color, PGraphics _frame)
  {
    myColor = _color;
    myPoint = _point;
    myFrame = _frame;
  }
  
  public void Draw()
  {
    if ( myFrame == null )
      Render();
    //translate(myPoint.x, myPoint.y, myPoint.z);
    blend(
      myFrame, 0, 0, width, height, 
      (int)myPoint.x, (int)myPoint.y, (int)MAX_RADIUS, (int)MAX_RADIUS, ADD);

    //image(myFrame,0,0);

    //translate(-myPoint.x, -myPoint.y, -myPoint.z);
  }
  
  private void Render()
  {        
    PGraphics pg = createGraphics(MAX_RADIUS*4, MAX_RADIUS*4, P3D);
    pg.beginDraw();
    pg.background(0,0,0,0);
    pg.noStroke();
    
    int step = GetStep();
    for ( int ring=(int)numRings; ring > 0; ring-- )
    {
      int _radius = PApplet.parseInt( (ring*step * MAX_RADIUS) / numRings );
      Colour _color = myColor.GetDarkened( ring*step );
      pg.fill(_color.r, _color.g, _color.b, _color.a);
      pg.ellipse(MAX_RADIUS*2, MAX_RADIUS*2, _radius, _radius);
    }    
    pg.endDraw();
    myFrame = pg;
  }
  
  public PGraphics GetFrame()
  {
    return myFrame;
  }
  
  public void Move()
  {
    myPoint.Move();
  }
  
  public void Move(Vertex _point)
  {
    myPoint.x = _point.x;
    myPoint.y = _point.y;
    myPoint.z = _point.z;
    myPoint.Move();
  }
  
  public Vertex GetVertex()
  {
    return myPoint;
  }
  public Colour GetColor()
  {
    return myColor;
  }
  
  private int GetStep()
  {
    /*
    if ( numRings < 256 )
      numRings += RING_INCREASE_AMOUT;
      */
    return (int)(256 / numRings);
  }  
}

class Explosion
{
  private ArrayList<FCircle> myCircles = new ArrayList<FCircle>();
  private final float MAX_OFFSET = 5f;
  private final int MAX_PARTICLES = 100;
  private boolean growing=true;
  private float nextAngle = 0.0f;
  private final float ANGLE_CHANGE = TWO_PI / MAX_PARTICLES;
  
  public Explosion()
  {
    myCircles.add(new FCircle());
  }
  
  public void Draw()
  {

    for ( int i=0; i < myCircles.size(); i++ )
    {
      FCircle thisCircle = myCircles.get(i);
      thisCircle.Draw();
      nextAngle += ANGLE_CHANGE;
      nextAngle %= TWO_PI;
      
      Vertex p = GetRelativeVertex(myCircles.get(0).GetVertex(), nextAngle, i );
      
      if ( i == 0 )
        thisCircle.Move();
      else
        thisCircle.Move(p);
      
    }
  }
  
  private Vertex GetRelativeVertex(Vertex origin, float angle, float _length)
  {
    return new Vertex(origin.x + _length*cos(angle), origin.y + _length*sin(angle), origin.z);
  }
  
  public void Grow()
  {
    if ( !growing )
      Shrink();
    else 
    {
      FCircle lastCircle = myCircles.get(myCircles.size()-1);
      Vertex v = lastCircle.GetVertex();
      Vertex newVertex = new Vertex(
        v.x + random(-MAX_OFFSET, MAX_OFFSET),
        v.y + random(-MAX_OFFSET, MAX_OFFSET),
        v.z + random(-MAX_OFFSET, MAX_OFFSET) );
        
      myCircles.add(new FCircle(newVertex, lastCircle.GetColor(), lastCircle.GetFrame() ) );
    }
    
    if ( myCircles.size() > MAX_PARTICLES )
      growing=false;  
  }
  
  private void Shrink()
  {
    myCircles.remove(1);
    if ( myCircles.size() < 2 )
      growing=true;
  }
  
}
  static public void main(String args[]) {
    PApplet.main(new String[] { "--bgcolor=#F0F0F0", "sketch_sep04b" });
  }
}
