ArrayList<Fish> fishes = new ArrayList<Fish>();


void setup() {
  size(800, 600);
  
  for(int i = 0; i < 20; i++) {
    Fish currentFish = new Fish(random(50, 750), random(50, 550));
    currentFish.setAngle(random(0, 360));
    currentFish.swimForward();
    fishes.add(currentFish);
      
  }
  
}

void draw() {
  
  for(Fish fish : fishes) {
    fish.update();
  }
  background(0, 119, 190);
  for(Fish fish : fishes) {
    fish.draw();
  }
}
class Fish {
  private float x;
  private float y;
  private PVector position;
  private PVector velocity;
  private FishNode head;
  private float angle;
  
  public Fish(float x, float y) {
    float startingTheta = random(0, (float)(2 * Math.PI));
    FishNode childNode = new FishNode(null, 7, 7, 20, 0, 0.3, startingTheta);
    childNode = new FishNode(childNode, 15, 15, 30, 0, 0.4, (float)(startingTheta + Math.PI / 4));
    head = new FishNode(childNode, 20, 20, 0, 0, 0, 0);
    position = new PVector(x, y);
    velocity = new PVector();
    
    this.angle = 0; 
  }
  public void setAngle(float angle) {
    this.angle = angle;
  }
  public void swimForward() {
    velocity.x = -cos(radians(angle));
    velocity.y = -sin(radians(angle));
  }
  
  public void update() {
    position.add(velocity);
    
    if(position.x < 0) {
     position.x = 0;
     angle += random(-90, 90);
     swimForward();
    }
    if(position.x > 800) {
     position.x = 800;
     angle += random(-90, 90);
     swimForward();
    }
    
    if(position.y < 0) {
      position.y = 0;
     angle += random(-90, 90);
     swimForward();
    }
    
    if(position.y > 600) {
      position.y = 600;
     angle += random(-90, 90);
     swimForward();
    }
    head.update();
  }
  
 public void draw() {
   pushMatrix();
     translate(position.x, position.y);
     pushMatrix();
       rotate(radians(angle));
       head.draw(null);
     popMatrix();
   popMatrix();
 } 
}
class FishNode {
 private FishNode child;
 public float x;
 public float y;
 private float width;
 private float height;
 private float theta;
 private float swimOffset;
 private float flopDirection;
 private float amplitude;
 
 public FishNode(FishNode child, float width, float height, float x, float y, float amplitude, float theta) {
  this.child = child; 
  this.width = width;
  this.height = height;
  this.x = x;
  this.y = y;
  this.theta = theta;
  swimOffset = 0.0f;
  flopDirection = 1;
  this.amplitude = amplitude;
  
 }
 
 public void setTheta(float theta) {
   this.theta = theta;
 }
 
 public float getTheta() {
   return theta;
 }
 
 public float getSine() {
   return amplitude * sin(theta);
 }
 
 public float getX() {
   return x;
 }
 
 public float getY() {
    return y;
 }
 
 public float getHeight() {
   return height;
 }
 
 public float getWidth() {
   return width;
 }
 
 public float getTopY() {
   return getHeight() / 2;
 }
 
 public float getBottomY() {
   return -getHeight() / 2;
 }
 
 public void update() {
    theta += 0.1;
    if(child != null) {
    child.update();
    }
 }
 
 public void draw(FishNode parent) {
   fill(0,0,0,0);
  strokeWeight(2);
  stroke(200,200,200,255);
  pushMatrix();
  rotate(getSine());
  if(parent != null) {
    line(0, parent.getTopY(), getX(), getTopY());
    line(0, parent.getBottomY(), getX(), getBottomY());
  }
  translate(getX(), getY()); 
  ellipse(0, 0, width, height);
  if(child != null) {
    child.draw(this);
  }
  popMatrix();
 }
}
