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 forms2 extends BApplet {
Figure[] figures = null;

void setup()
{
  size(100,500);
  background(255,255,255);
  noStroke();
  ellipseMode(CENTER_DIAMETER);

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

  c[k++] = new ColorTuple(234,212,216);
  c[k++] = new ColorTuple(246,230,188);
  c[k++] = new ColorTuple(241,217,198);
  c[k++] = new ColorTuple(209,213,219);
  c[k++] = new ColorTuple(221,216,196);
  c[k++] = new ColorTuple(245,212,205);
  c[k++] = new ColorTuple(248,237,236);
  c[k++] = new ColorTuple(246,236,232);
  c[k++] = new ColorTuple(229,238,241);
  c[k++] = new ColorTuple(241,239,204);

  figures = new Figure[16];
  for (int i = 0; i < 16; i++)
  {
    int figureSize = (int)(random(80)) + 10;
    int x = (int)(random(100));
    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(4);
      }

    }
    
  }

  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 xVel = 0.0f;
  private float yVel = 0.0f;
  private float x;
  private float y;
  private float friction = 1.05f;
  private ColorTuple myColor;
  private float mySize;
  private int myUpperBound;

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

  public float moveX()
  {
    x += xVel;
    if (collision(x))
    {
      xVel = -xVel;
      x+= xVel;
    }
    xVel /= friction;
    return x;
  }

  public float moveY()
  {
    y += yVel;
    if (collision(y))
    {
      yVel = -yVel;
      y+= yVel;
    }
    yVel /= friction;
    return y;
  }

  private boolean collision(float value)
  {
    boolean result = false;
    if (abs(value) >= myUpperBound)
    result = true;

    return result;
  }

  public float velocity()
  {
    float totalVel = abs(xVel) + abs(yVel);
    return totalVel;
  }

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

  public void draw()
  {
    moveX();
    moveY();
    myColor.setFill();
    ellipse(x, y, mySize, mySize);
  }

  public void kick(float bounceFactor)
  {
    xVel = (random(bounceFactor*2) - bounceFactor)/4.0f;
    yVel = (random(bounceFactor*2) - bounceFactor)/4.0f;
  }

  public void setColor(ColorTuple colorTuple)
  {
    myColor = 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 void setFill()
  {
    fill(myR, myG, myB);
  }

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

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


}
