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 levitatedTentacle14 extends BApplet {


 
// Set the font and its size (in units of pixels) 

int xWidth = 800, yHeight = 500;

int total = 0;
Tentacle[] tentacle =  new Tentacle[1500];

boolean clear = true;
boolean click = false;

int counter = 0;

void setup() {

  for (int i=0; i<tentacle.length; i++){
    tentacle[i] = new Tentacle();
  }
  size(800,500);
  noBackground();
  noStroke();
  fill(0);
  rect(0,0,width,height);
  ellipseMode(CENTER_DIAMETER);
}
 
void loop() {
  drawState();

  for (int i=0; i<total; i++){
    tentacle[i].move();
  }
} 

void mouseDragged () {

  if(total >= 1500){
    total = 1500;
  } else {
    total = total + 1;
  }
}

void drawState(){
  if (clear){
    fill(0);
    noStroke();
    rect(0,0,width,height);
  }
}

void keyReleased () {
  if (key == ' ' && clear) { 
    clear = false;
  } else if (key == ' ' && !clear) {
    clear = true;
  }
}

class Tentacle {
  int numNodes = 50;
  float head = 1;
  float girth = 2;
  float baseSpeed = random(0.2f,0.6f);
  float speedCoefficient;
  float friction = .5f;
  float muscleRange = 45;
  float muscleFreq = .1f;
  float theta;
  float thetaMuscle;
  float count = 0;
  float xPos, yPos;
  float tv;
  float angle, mAngle, m2Angle;
  float xOffset = width/2;
  float yOffset = height/2;
  float tvVar;
  float tvRange = random(3,8);
  float dx, dy, d;

  SnakeHead[] node = new SnakeHead[numNodes];

  Tentacle () {

  }

  void move () {
    if(count == 0){
      xPos = mouseX;
      yPos = mouseY;
      for (int n=0; n<numNodes; n++){
        SnakeHead sh = new SnakeHead();   
        node[n] = sh;

        if (n > 0){
          node[n].setX(((pmouseX - mouseX)*(n/numNodes)) + mouseX);
          node[n].setY(((pmouseY - mouseY)*(n/numNodes)) + mouseY);
        } else {
          node[n].setX(mouseX);
          node[n].setY(mouseY);
        }
      } 
      angle = atan2((mouseY-pmouseY), (mouseX-pmouseX));
      theta = angle * (180/PI);
    }

    mAngle = atan2((mouseY-node[0].getY()), (mouseX-node[0].getX()));
    mAngle = mAngle * (180/PI);
    
    m2Angle = atan2((node[0].getY()-node[1].getY()), (node[0].getX()-node[1].getX()));
    m2Angle = m2Angle * (180/PI);
    
    dx = mouseX - node[0].getX();
    dy = mouseY - node[0].getY();
    d = sqrt(sq(dx) + sq(dy));
    speedCoefficient = baseSpeed - d / 2000;
    
    
    //System.out.println("mAngle = " + mAngle + " and m2Angle = " + m2Angle);
    
    if (mAngle > m2Angle){
      tvVar = random(0,tvRange);
    } else if (mAngle < m2Angle){
      tvVar = random(-tvRange,0);
    }
    
    tv = tv + tvVar;
    theta = theta + tv;
    tv = tv * friction;
      
    if (node[0].getX() > width*4){
      xPos = xPos - (width*5);
      for (int n=0; n<numNodes; n++){
        node[n].setX(node[n].getX() - (width*5));
      }
    } else if (node[0].getX() < -width*3){
      xPos = xPos + (width*5);
      for (int n=0; n<numNodes; n++){
        node[n].setX(node[n].getX() + (width*5));
      }
    } else {
      node[0].setX(head * cos(PI/180 * theta) + xPos);
      count = count + muscleFreq;
      thetaMuscle = muscleRange * sin(count);
      node[1].setX(-head * cos(PI/180 * (theta+thetaMuscle)) + xPos);
    }
    
    if (node[0].getY() > height*4){
      yPos = yPos - (height*5);
      for (int n=0; n<numNodes; n++){
        node[n].setY(node[n].getY() - (height*5));
      }
    } else if (node[0].getY() < -height*3){
      yPos = yPos + (height*5);
      for (int n=0; n<numNodes; n++){
        node[n].setY(node[n].getY() + (height*5));
      }
    } else {
      node[0].setY(head * sin(PI/180 * theta) + yPos);
      count = count + muscleFreq;
      thetaMuscle = muscleRange * sin(count);
      node[1].setY(-head * sin(PI/180 * (theta+thetaMuscle)) + yPos);
    }
    
    
    for (int i=2; i<numNodes; i++){
      float dx = node[i].getX() - node[i-2].getX();
      float dy = node[i].getY() - node[i-2].getY();
      float d = sqrt(sq(dx) + sq(dy));
      
      node[i].setX(node[i-1].getX() + (dx * girth) / d);
      node[i].setY(node[i-1].getY() + (dy * girth) / d);
      
      if (i == 2){
        xPos = xPos - dx * speedCoefficient;
        yPos = yPos - dy * speedCoefficient;
        
        //if ((xPos<100) || (xPos>500) || (yPos<100) || (yPos>500)){
        //  destroy();
        //}
      }
    }
   // strokeWidth(1);
    for (int i=2; i<numNodes; i++){
      //stroke(0,(numNodes-i)*2 + 51,0);
      stroke((255 - dx)/((i+3)/5),(255 - dy)/((i+3)/5),(255-d)/((i+3)/5));
      point(node[i].getX(), node[i].getY());
      stroke((dx)/((i+3)/5),(255-d)/((i+3)/5),(255-dx)/((i+3)/5));
      point(node[i].getX()-1, node[i].getY()-1);
    }
  }
  
  void destroy () {
    numNodes = 0;
    head = 0;
    girth = 0;
    speedCoefficient = 0;
    friction = 0;
    muscleRange = 0;
    muscleFreq = 0;
    theta = 0;
    node = null;
  }
}

class SnakeHead {
  float x;
  float y;
  float getX () {
    return x;
  }
  float getY () {
    return y;
  }
  void setX (float val) {
    x = val;
  }
  void setY (float val) {
    y = val;
  }
}


}