/*"The Party" - Rick Gilliland
  There are three balls: Lovely, Grumpy, and Drunky.
  Lovely and Grumpy are going out, and Drunky is a
  mutual friend.  Lovely, however, likes the mouse
  quite a bit, and constantly seeks it out. Grumpy,
  being a jealous fellow, does not like when Lovely
  fraternizes with others.  If the mouse and Lovely
  get too close, Grumpy gets angry and breaks up the
  engagement.  Drunky is an odd chap.  He has had
  far too much to drink this evening.  As such, he
  stumbles about the room. He can talk some sense
  into Grumpy and calm him down if he's near by, and
  if the mouse is pressed, Drunky will go seek out
  Grumpy.  If Drunky walks over to Lovely, however,
  Grumpy will get just as mad as if the mouse were
  talking to her, and Grumpy will charge his
  good friend.
*/

BallA grumpy;
BallB lovely;
BallC drunky;

void setup(){
  size(400,400);
  background(0);
  smooth();
  grumpy = new BallA(width/2, height/2, 60);
  lovely = new BallB(width/2+80, height/2-20, 60);
  drunky = new BallC(width/2-80, height/2-20, 60);
}

void draw(){
  background(0);
  translate(0,height);
  scale(1,-1);
  
  //Check to see if the mouse is near Lovely
  if (dist(mouseX, mouseY, lovely.pos.x, 400-lovely.pos.y) < lovely.rad + 10){
    grumpy.angry = true;
    grumpy.targetMouse = true;
  
  //Check to see if drunky is near lovely
  } else if (dist(lovely.pos.x, lovely.pos.y, drunky.pos.x, drunky.pos.y) < drunky.rad*2 +20){
    grumpy.angry = true;
    grumpy.targetMouse = false;
  } else {
    grumpy.angry = false;
  }
  
  //If drunky is near grumpy, grumpy chills.
  if (dist(grumpy.pos.x, grumpy.pos.y, drunky.pos.x, drunky.pos.y) < drunky.rad*2 +20){
    grumpy.angry = false;
    grumpy.rage = false;
    grumpy.anger = 0;
  }
  
  collide(grumpy, lovely);
  collide(grumpy, drunky);
  collide(drunky, lovely);
  
  grumpy.update();
  lovely.update();
  drunky.update();
}

class Ball{
  Vector pos, vel;
  float rad, dia;
  
  void wallCheck(){
    if( ((0 + rad) > pos.x) || ((width - rad) < pos.x)){
      vel.x = -vel.x;
      pos.x = constrain(pos.x, 0+rad, width-rad);
    }
    if(((0 + rad) > pos.y) || ((height - rad) < pos.y)){
      vel.y = -vel.y;
      pos.y = constrain(pos.y, 0+rad, width-rad);
    }
  }
  
  void run(){
    wallCheck();
    pos.add(vel);
    vel.mult(.99);
    fill(255);
    noStroke();
  }
  
  void update(){
  }
}

class BallC extends Ball{

  BallC(float X, float Y, float Dia){
    pos = new Vector(X, Y);
    vel = new Vector();
    dia = Dia;
    rad = dia/2;
  }
  
  void update(){
    run();
    stumble();
    ellipse(pos.x, pos.y, dia, dia);
  }
  
  void stumble(){
    if (mousePressed){
      vel.add(random(-.2, .2), random(-.2, .2));
      Vector temp = new Vector(grumpy.pos.x - pos.x, grumpy.pos.y - pos.y);
      temp.normalize();
      temp.mult(.05);
      vel.add(temp);
    } else {
      vel.add(new Vector(random(-.3, .3), random(-.3, .3)));
    }
  }

}

class BallB extends Ball{
  
  BallB(float X, float Y, float Dia){
    pos = new Vector(X, Y);
    vel = new Vector();
    dia = Dia;
    rad = dia/2;
  }
  
  void update(){
    run();
    seek();
    ellipse(pos.x, pos.y, dia, dia);
  }
  
  void seek(){
    if(dist(mouseX, mouseY, pos.x, pos.y) > rad/2 ){
      Vector temp = new Vector(mouseX - pos.x, (height-mouseY) - pos.y);
      temp.normalize();
      temp.mult(.01);
      vel.add(temp);
      vel.add(new Vector(random(-.05, .05), random(-.05, .05)));
    }
  }
}

class BallA extends Ball{
  boolean angry, rage, targetMouse;
  float anger;
  int rageLocker;
  
  BallA(float X, float Y, float Dia){
    pos = new Vector(X, Y);
    vel = new Vector();
    dia = Dia;
    rad = dia/2;
  }
  
  void update(){
    run();
    rageLocker++;
    pushMatrix();
    if (rageLocker > 90){
      if (angry){
        rage = true;
        translate(random(-5, 5), random(-5, 5));
        if (anger < 30){
          anger += .25;
        } else {
          rage();
        }
      } else if(rage){
        rage();
      }
    }
    
    ellipse(pos.x, pos.y, dia, dia);
    popMatrix();
  }
  
  void rage(){
      rage = false;
      if (targetMouse){
        vel = new Vector(mouseX - pos.x, (height-mouseY) - pos.y);
      } else {
        vel = new Vector(drunky.pos.x - pos.x, drunky.pos.y - pos.y);
      }
      vel.normalize();
      vel.mult(grumpy.anger);
      anger = 0;
      rageLocker = 0;
  }

}

void collide(Ball A, Ball B){
  float disX = A.pos.x - B.pos.x;
  float disY = A.pos.y - B.pos.y;
  float disR = A.rad + B.rad;
  if(disR*disR > (disX*disX + disY*disY)){  //Cheapest collision check we can do, because this is run often
    Vector U1x, U1y, U2x, U2y;              //Temp component Vectors
    Vector temp = new Vector(disX, disY);   //Axis Vector that we project on 
    
    //While this is a near mimic of the above if, this is more costly.
    //As such, we only run it when we know there is a collision.
    float over = (disR - sqrt(disX*disX + disY*disY))*.5; //Find the overlap
    
    temp = temp.unit();                     // Unit vector.
    A.pos.add(temp.prod(over));             // Push A along the axis to outside of B.
    
    U1x = temp.prod(temp.dot(A.vel));       // Projections
    U1y = A.vel.dif(U1x);
    
    temp.mult(-1);                          // Flip the direction of the axis
    B.pos.add(temp.prod(over));             // Push B along the axis to outside of A.
    
    U2x = temp.prod(temp.dot(B.vel));
    U2y = B.vel.dif(U2x);
    
    //Final Velocities.
    A.vel.set(U2x);
    A.vel.add(U1y);
    A.vel.mult(.75);
    
    B.vel = new Vector();
    B.vel.set(U1x);
    B.vel.add(U2y);
    B.vel.mult(.75);
    
    /*
    Vector temp = new Vector(disX, disY);
    temp.mult(.1);
    A.vel.add(temp);
    temp.mult(-1);
    B.vel.add(temp);
    */
  }
}

class Vector{
  float x, y;
  
  Vector(){
    x = 0;
    y = 0;
  }
  
  Vector(Vector V){
    x = V.x;
    y = V.y;
  }
  
  Vector(float X, float Y){
    x = X;
    y = Y;
  }
  
  void set(Vector V){
    x = V.x;
    y = V.y;
  }
  
  void add(Vector V){
    x += V.x;
    y += V.y;
  }
  
    void add(float X, float Y){
    x += X;
    y += Y;
  }
  
  Vector sum(Vector V){
    Vector temp = new Vector(x + V.x, y + V.y);
    return temp;
  }
  
  void sub(Vector V){
    x -= V.x;
    y -= V.y;
  }
  
  Vector dif(Vector V){
    Vector temp = new Vector(x - V.x, y - V.y);
    return temp;
  }
  
  void mult(float T){
    x *= T;
    y *= T;
  }
  
  Vector prod(float T){
    Vector temp = new Vector(x*T, y*T);
    return temp;
  }
  
  float magni(){
    float magnitude = sqrt(x*x+y*y);
    return magnitude; 
  }
  
  Vector unit(){
    Vector temp = new Vector(x,y);
    temp.mult(1/magni());
    return temp;
  }
  
  void normalize(){
    this.mult(1/magni());
  }
  
  float dot(Vector V){
    float temp = x*V.x + y*V.y;
    return temp;
  }
}
Exercise 08: Give three circles unique personalities through their qualities of response. For example, one circle can be frightened of the mouse and another affectionate. One circle can become curious about the mouse when it is pressed, but pay no attention otherwise.