import java.applet.*; 
import java.awt.*; 
import java.awt.image.*; 
import java.awt.event.*; 
import java.io.*; 
import java.net.*; 
import java.text.*; 
import java.util.*; 
import java.util.zip.*; 

public class forms3 extends BApplet {
Figure[] figures = null;

void setup()
{
  size(200,500);
  background(255,255,255);
  noStroke();
  ellipseMode(CENTER_DIAMETER);
  framerate(30);

  ColorTuple[] c = new ColorTuple[9];
  int k = 0;

  c[k++] = new ColorTuple(254,167,88);
  c[k++] = new ColorTuple(78,43,5);
  c[k++] = new ColorTuple(125,109,122);
  c[k++] = new ColorTuple(164,113,132);
  c[k++] = new ColorTuple(255,186,75);
  c[k++] = new ColorTuple(255,236,160);
  c[k++] = new ColorTuple(69,33,1);
  c[k++] = new ColorTuple(200,166,138);
  c[k++] = new ColorTuple(194,101,8);
  
  figures = new Figure[30];
  for (int i = 0; i < figures.length; i++)
  {
    int figureSize = (int)(random(80)) + 10;
    int x = (int)(random(200));
    int y = (int)(random(500));
      figures[i] = new Figure(6,figureSize, x, y, c);
  }
  
}

void loop()
{
  //  f.draw();
  for (int i = 0; i < figures.length; i++)
  {
    figures[i].draw();
  }
}

class Figure
{
  public Figure(int numberOfForms, int figureSize, int x, int y, ColorTuple[] palette)
  {
    myNumForms = numberOfForms;
    myX = x;
    myY = y;
    myFigureSize = figureSize;
    myParticles = new Particle[numberOfForms];
    float currentSize = myFigureSize;
    myPalette = palette;
    ColorTuple figureColor = pickColor();
    ColorTuple currentColor = figureColor;
    float sizeFactor = myFigureSize / 10;

    for (int i = 0; i < numberOfForms; i++)
    {
      int upperBound  = 0;
      upperBound = (int)(figureSize/3.5f);
      myParticles[i] = new Particle(currentSize,currentColor,upperBound);
      if (currentColor != white)
      {
        currentColor = white;
      }
      else
      {
        currentColor = figureColor;
        currentSize -= sizeFactor;
      }
    }
  }

  public void draw()
  {
    boolean shouldKick = isTimeToKick();
    ColorTuple newColor = null;
    if (shouldKick)
    {
      newColor = pickColor();
    }
    for (int i=0; i < myNumForms; i++)
    {
      Particle p = myParticles[i];
      push();
      translate(myX, myY);
      push();
      p.draw();
      pop();
      pop();

      if (shouldKick)
      {
        if (p.getColor() != white)
        {
          p.setColor(newColor);
        }
        p.kick(6);
      }

    }
    
  }

  private ColorTuple pickColor()
  {
    int colorIndex = (int)(random(myPalette.length));
    ColorTuple figureColor = myPalette[colorIndex];
    return figureColor;
  }

  private boolean isTimeToKick()
  {
    boolean shouldKick = false;
    boolean allResting = true;
    for (int i=0; i < myNumForms && allResting; i++)
    {
      if (myParticles[i].isMoving())
      {
        allResting = false;
      }
    }
    if (allResting)
    {
      if (millis() > kickNow)
      {
        shouldKick = true;
      }
    }
    return shouldKick;
  }

  private float myX;
  private float myY;
  private int   myNumForms;
  private int   myFigureSize;
  private Particle[] myParticles;
  private long kickNow = 0;
  private ColorTuple[] myPalette;
    ColorTuple white = new ColorTuple(255,255,255);
}

class Particle
{
  private float sVel = 0.0f;
  private float x;
  private float y;
  private float s;
  private float myAngle;
  private float friction = 1.03f;
  private ColorTuple myColor;
  private float mySize;
  private int myUpperBound;

  public Particle(float aSize, ColorTuple aColor, int upperBound)
  {
    y = 0;
    myColor = new ColorTuple(aColor);
    mySize = aSize;
    myUpperBound = upperBound;
  }

  public float moveS()
  {
    s += sVel;
    sVel /= friction;
    return s;
  }

  public float velocity()
  {
    return sVel;
  }

  public boolean isMoving()
  {
    boolean moving = false;
    if( velocity() > .001f)
      moving = true;
    return moving;
  }

  public void draw()
  {
    moveS();
    myColor.setFill();
    push();
      translate(x,y);
      rotate(myAngle);
      translate(0,s);
      rect(0,0, mySize, mySize);
    pop();
    if (sVel < .5f)
    {
      myColor.fade();
    }
//  ellipse(x, y, mySize, mySize);
  }
  
  public void kick(float bounceFactor)
  {
    myAngle = random(PI*2);
    s = 0;
    sVel = (random(bounceFactor*2) - bounceFactor)/4.0f;
  }

  public void setColor(ColorTuple colorTuple)
  {
    myColor = new ColorTuple(colorTuple);
  }
  
  public ColorTuple getColor()
  {
    return myColor;
  }

}

class ColorTuple implements P5Color
{
  private int myR;
  private int myG;
  private int myB;

  public ColorTuple(int r, int g, int b)
  {
    myR = r;
    myG = g;
    myB = b;
  }
  
  public ColorTuple(ColorTuple original)
  {
    myR = original.myR;
    myB = original.myB;
    myG = original.myG;
  }

  public void setFill()
  {
    fill(myR, myG, myB);
  }

  public void setStroke()
  {
    fill(myR, myG, myB);
  }
  
   public void fade()
   {
     myR+= diffTo255(myR);
     myB+= diffTo255(myB);
     myG+= diffTo255(myG);
     
   }
   
   private int diffTo255(int value)
   {
     return (255 - value) / 20;
   }
}

public interface P5Color
{
  public void setFill();
  public void setStroke();
}


}
