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_sep04c extends PApplet {

final int NUM_EXPLOSIONS = 20;
Explosion[] fireworks = new Explosion[NUM_EXPLOSIONS];


public void setup()
{
  size(800,600,P3D);
  background(0);  
  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()
  {
    myVector = new Vector(true);
  }
  
  public void Move()
  {
    x += myVector.x;
    y += myVector.y;
    z += myVector.z;
    x %= width;
    y %= height;
    z %= super.MAX_Z;
  }
}

class Vector extends Coord
{
  private final float MAX_RANDOM_OFFSET = 3f;
  
  public Vector()
  {
    super();
  }
  public Vector(float _x, float _y, float _z)
  {
    super(_x,_y,_z);
  }
  public Vector(boolean random)
  {
    super();
    if ( random )
    {
      x = random(-MAX_RANDOM_OFFSET, MAX_RANDOM_OFFSET);
      y = random(-MAX_RANDOM_OFFSET, MAX_RANDOM_OFFSET);
      z = random(-MAX_RANDOM_OFFSET*0.005f, MAX_RANDOM_OFFSET*0.005f);      
    }
  }
}

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);
    float drawSize = MAX_RADIUS / myPoint.z;
    
    blend(myFrame, 0, 0, width, height, 
      (int)myPoint.x-MAX_RADIUS/2, (int)myPoint.y-MAX_RADIUS/2, 
      (int)drawSize, 
      (int)drawSize, 
      SCREEN);
    //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 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;
  
  public Explosion()
  {
    myCircles.add(new FCircle());
  }
  
  public void Draw()
  {
    for ( int i=0; i < myCircles.size(); i++ )
    {
      FCircle thisCircle = myCircles.get(i);
      thisCircle.Draw();
      if ( i != 0 )
        thisCircle.Move();
    }
  }
  
  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 )
      myCircles.remove(1);
      //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_sep04c" });
  }
}
