/*

TODO:

DONE;
ydrift 
xdrift
draw 
think of line ahead of time and stop drawing if too low
draw faster
drawslower
compare x pos to very original!
try processing.js
*/

boolean drawFast = false;

int sz = 500;
Line goalLine;
Line currentLine;
Line firstLine;

float satur = .95;
float lowsat = .5;

ArrayList<Line> oldLines = new ArrayList<Line>();

Drafter drafters[] = new Drafter[3];
int currentDrafter = -1;
int pointCount;

void setup(){
  smooth();
  strokeWeight(2);
  cursor(CROSS);
  frameRate(500);
  drafters[0] = new Drafter(color(255*satur,255*lowsat,255*lowsat));
  drafters[1] = new Drafter(color(255*satur,255*satur,255*lowsat));
  drafters[2] = new Drafter(color(255*lowsat,255*lowsat,255*satur));
  
  stroke(255*lowsat);
  
  
   size(500,500); 
   background(255);
}

boolean done = false;

boolean finishedSlowDraw = true;


void keyPressed(){
 drawFast = !drawFast; 

}


void draw(){
  
   if(done) return;
  
  if(currentDrafter != -1){
    if(!drafters[currentDrafter].doneDrawing()){
     if(drawFast){
       drafters[currentDrafter].drawAll();
     }else{
        drafters[currentDrafter].drawSeg();
     }
    } else {  
      done = ! startDrafter();        
    }
  } 
}

boolean startDrafter(){
  currentDrafter++;
  if(currentDrafter >= 3) currentDrafter = 0;
  oldLines.add(currentLine);
  goalLine = currentLine;
 
  drafters[currentDrafter].start();
  return drafters[currentDrafter].thinkWholeLine();
}

void mousePressed(){
  currentDrafter = -1;
  background(255);
  currentLine = new Line(mouseX,mouseY,color(30));
  firstLine = currentLine;
  goalLine = null;
  done = true;
}
void mouseDragged(){
  if(goalLine != null) mousePressed();
  if(currentLine != null){
     currentLine.add(mouseX,mouseY,true);
  } 
   
}
void mouseReleased(){

  if(currentLine != null){
    done = false;
     pointCount = currentLine.points.size(); 
     currentDrafter = -1;
     startDrafter();
  }
}


class Line{
  ArrayList<Point> points = new ArrayList<Point>();
  color c;
  Line(float x, float y, color pc){
     points.add(new Point(x,y)); 
     c = pc;
  }
  Line(Point p , color pc){
     points.add(p); 
     c = pc;
  }
  
  
  
  void add(Point p, boolean draw){
      points.add(p);           
      if(draw) drawLatest();
  }
  void add(float x, float y, boolean draw){
      points.add(new Point(x,y));           
      if(draw) drawLatest();
  }
  
  void drawLatest(){
          stroke(c);
      if(points.size() >= 2){
         Point p2 = points.get(points.size()-1); 
         Point p1 = points.get(points.size()-2);
         line(p1.x,p1.y,p2.x,p2.y);
    }
  }
  
  void drawSeg(int i){
          stroke(c);
          if(i <= 0 || i >= points.size()) return;
         Point p2 = points.get(i); 
         Point p1 = points.get(i-1);
         line(p1.x,p1.y,p2.x,p2.y);
    
  }
  
  
  void draw(){
    stroke(c);
     for(int i = 0; i < points.size()-1;i++){
        Point p1 = points.get(i);
        Point p2 = points.get(i+1);
        line(p1.x,p1.y,p2.x,p2.y);
     } 
  }

}

class Point{
  float x,y;
   Point(float px, float py){
     x = px;
     y = py;
   } 
}


class Drafter{
  color c;
  int p = 0;
 float COREYDRIFT = 4;
 float COREYDRIFTMAX = 1;
 float ydrift = COREYDRIFT;
   float ydriftspeed = 0;
 float COREXDRIFT = 0;
 float COREXDRIFTMAX = 1;
 float xdrift = COREXDRIFT;
   float xdriftspeed = 0;
 float XDRIFTMAX = 10;
 
 
  Drafter(color pc){
    c = pc;
  }
  void start(){
     currentLine = new Line(makeNewPoint(0),c); 
    p = 0;  
}


boolean thinkWholeLine(){
  currentDrawLine = 0;
  if(thinkIt() == false) return false; //hit bottom
  
  
  return true;
}


int currentDrawLine = 0;

boolean doneDrawing(){
//  println("COMPARE "+currentDrawLine+" to "+ currentLine.points.size());
 return currentDrawLine >= currentLine.points.size(); 
}
void drawSeg(){
  currentDrawLine++;
  currentLine.drawSeg(currentDrawLine);
}
void drawAll(){
  currentLine.draw();
  currentDrawLine = currentLine.points.size(); 
}
boolean thinkIt(){
 while(advance()); 
 if(anyTooLow()) return false;
 return true;
}

boolean anyTooLow(){
   for(int i = 0; i < currentLine.points.size();i++){
     Point p = currentLine.points.get(i); 
     if(p.y >= sz-1) return true;
   } 
   return false;
}



//return false if we're done, otherwise tue
  boolean advance(){
    p++;
    
    if(p >= goalLine.points.size()) return false;
    currentLine.add(makeNewPoint(p),false);
  return true;
  }
  Point makeNewPoint(int gp){
    Point goalPoint = goalLine.points.get(gp);
    float x = goalPoint.x+xdrift;// + random(-.2,2);
    
    
    
    if(gp == 0){
      if(x > firstLine.points.get(gp).x){
        x = firstLine.points.get(gp).x;
      }
    }
    if(gp >= firstLine.points.size()){
      if(x < firstLine.points.get(gp).x){
        x = firstLine.points.get(gp).x;
      }
    }
    
    
    float y = goalPoint.y+ydrift;
    ydrift += ydriftspeed;
    ydriftspeed += random(-.1,.1);
    
    if(ydrift < COREYDRIFT - COREYDRIFTMAX){
       ydriftspeed = abs(ydriftspeed)/2; 
    }
    if(ydrift > COREYDRIFT + COREYDRIFTMAX){
       ydriftspeed = -abs(ydriftspeed)/2; 
    }
    
    xdrift += xdriftspeed;
    xdriftspeed += random(-.1,.1);
    
    Point firstPoint = firstLine.points.get(gp);
    
    float xOff = x - firstPoint.x;
    
    if(xOff < -XDRIFTMAX || xdrift < COREXDRIFT - COREXDRIFTMAX){
       xdriftspeed = abs(xdriftspeed)/2; 
    }
    if(xOff > XDRIFTMAX || xdrift > COREXDRIFT + COREXDRIFTMAX){
       xdriftspeed = -abs(xdriftspeed)/2; 
    }
    
    return new Point(x,y);    
  }
}