import processing.core.*; 
import processing.data.*; 
import processing.event.*; 
import processing.opengl.*; 

import moonlander.library.*; 
import ddf.minim.*; 

import java.util.HashMap; 
import java.util.ArrayList; 
import java.io.File; 
import java.io.BufferedReader; 
import java.io.PrintWriter; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.io.IOException; 

public class stickman extends PApplet {

/* 
 * Code for starting a demo project that
 * uses GNU Rocket and Moonlander for
 * syncing.
 *
 * You must install Moonlander as a library
 * into Processing before starting development.
 */


// Minim is needed for the music playback
// (even when using Moonlander)


// These control how big the opened window is.
// Before you release your demo, set these to 
// full HD resolution (1920x1080).
int CANVAS_WIDTH = 1000;
int CANVAS_HEIGHT = 600;
int ground_y = CANVAS_HEIGHT - (int)(CANVAS_HEIGHT*0.05f);

// Our public Moonlander instance
Moonlander moonlander;


public void draw_lightning(PVector src,PVector dest)
{ 
  stroke(255);
  strokeWeight(8);
 // pushMatrix();
  //translate(src.x,src.y);
  PVector curr = new PVector(src.x,src.y);
  PVector newP = new PVector(src.x,src.y);
  int done = 0;
  int i = 0;
  float len,ang;  
  float targAng ; 
  while(done==0){
    if(dest.x> curr.x) {
      targAng= degrees(atan((dest.y-curr.y)/(dest.x-curr.x)));
    }else{
      targAng= 180 - degrees(atan((dest.y-curr.y)/(curr.x-dest.x)));
    }        
    //choose a random angle
    if(i%2 == 0){
       ang = random(30,150);
       len = random(5,10);
    } else {
       ang =  random(targAng-5,targAng+5);
       len = random(15,min(dest.dist(curr),100));
    }    
    newP.set(curr.x + len*cos(radians(ang)),curr.y + len*sin(radians(ang))) ;  
    line(curr.x,curr.y,newP.x,newP.y);
    //println(i,curr,newP,ang,len,targAng,dest);
    curr.x = newP.x;curr.y = newP.y;
    
    if(newP.dist(dest) < 20 || newP.y >= ground_y)
      done = 1;
    i+=1 ;
  }  
  strokeWeight(1);
  noStroke();
  //popMatrix();
}

public void draw_cloud(PVector loc)
{  
  noStroke();
  fill(255);
  pushMatrix();
  translate(loc.x,loc.y);
  scale(loc.z);
  ellipse(100, 100, 100, 100);
  ellipse(50, 100, 100, 100);
  ellipse(150, 100, 100, 100);
  ellipse(200, 100, 130, 100);
  ellipse(50, 50, 100, 70);
  ellipse(150, 50, 130, 70);
  popMatrix();
}
float [] x_index;
float [] y_index;
int INTENSITY= 100;
int relative_mov=0;
int init_rain=0;
public void let_it_rain(PVector loc,int first_Obst)
{
  relative_mov=(int)loc.x; //go until canvas width
  int inside = color(255, 255, 255,128);
  fill(inside);
  if(init_rain==0) //initialise the rain indices
  {
    x_index= new float[INTENSITY];
    y_index= new float[INTENSITY];
    for(int k=0; k<INTENSITY; k++)
    {
      x_index[k]= random(loc.x,loc.x+(250*loc.z));
      y_index[k]= random(loc.y,first_Obst);
    }
    init_rain= 1;
  }
  for(int k=0; k< INTENSITY; k++)
  {
    ellipse(x_index[k],y_index[k], 2, 4);
    y_index[k]= (y_index[k]+10);
    x_index[k]+=1;
    if(y_index[k] >= first_Obst)
    {
      x_index[k]= relative_mov+random(250*loc.z); // cloud end point
      y_index[k]= loc.y; //rain start point
    }
  }
}

public void tree(PVector l)
{
     stroke(0);
 
  // Let's pick an angle 0 to 90 degrees based on the mouse position
  float a = 20;
  pushMatrix();
  // Convert it to radians
  float theta = radians(a);
  // Start the tree from the bottom of the screen
  translate(l.x,l.y);
  rotate(l.z);
  // Draw a line 120 pixels
  //line(0,0,0,-120);
  fill(139,69,19);
  rect(-1,0,4,-120);
   // Move to the end of that line
  translate(0,-120);
  // Start the recursive branching!
  branch(80,theta);
  popMatrix();

}

public void ground() {

 fill(124 , 252,  0);
  rect(0, height-height*0.05f,width,height*0.05f);
}

public void branch(float h,float theta) {
  // Each branch will be 2/3rds the size of the previous one
  h *= 0.66f;
    
  // All recursive functions must have an exit condition!!!!
  // Here, ours is when the length of the branch is 2 pixels or less
  if (h > 2) {
    pushMatrix();    // Save the current state of transformation (i.e. where are we now)
    rotate(theta);   // Rotate by theta
    //line(0, 0, 0, -h);  // Draw the branch
    fill(0,255,0);
    rect(0,0,2,-h);
    translate(0, -h); // Move to the end of the branch
    branch(h,theta);       // Ok, now call myself to draw two new branches!!
    popMatrix();     // Whenever we get back here, we "pop" in order to restore the previous matrix state
    
    // Repeat the same thing, only branch off to the "left" this time!
    pushMatrix();
    rotate(-theta);
    //line(0, 0, 0, -h);
    //fill(0,255,0);
    rect(0,0,2,-h);
    translate(0, -h);
    branch(h,theta);
    popMatrix();
  }
}

public void fire(PVector loc){
  pushMatrix();
  translate(loc.x,loc.y);
  scale(loc.z);
  
  float t = random(10 * 0.3f);
  curveTightness(t);
  noStroke();
  fill(255 , 215,  0); 
  beginShape();

  curveVertex(0 ,0);
  curveVertex(-22 ,0);
  curveVertex(-35,-20);
  curveVertex(-62,-35);
  curveVertex(-22 ,-37);
  curveVertex(-35,-65);
  curveVertex(-15,-55);
  curveVertex(0, -85);
  curveVertex(15,-55);
  curveVertex(35,-65);
  curveVertex(22,-37);
  curveVertex(62,-35); 
  curveVertex(35,-20);
  curveVertex(22,-0);
  curveVertex(0,0);
  endShape();
  popMatrix();
}



class StickMan {
  // Some variables
  PShape man;
  PShape head;
  PShape body;
  PShape lHand;
  PShape rHand;
  PShape lLeg;
  PShape rLeg;
  PVector curr_loc = new PVector(0,0);
  float curr_theta;
  
  int hOx = 0;
  int hOy = 30;
  int lOx = 0;
  int lOy = 70;
  
  float lHa = 135;
  float rHa = 45;
  float lLa = 135;
  float rLa = 45;
  
  int handLen = 30;
  int legLen = 30;
  PVector vel = new PVector(0,0,0);
  
  PVector handRotVel = new PVector(0,0);
  PVector legRotVel = new PVector(0,0);

  PVector lHLim = new PVector(95,210);
  PVector rHLim = new PVector(-30,85);  
  PVector lLLim = new PVector(95,210);
  PVector rLLim = new PVector(-30,85);
  
  PVector v = new PVector(0,0);
  int running;
  float man_height;
  PVector run_targ = new PVector(0,0);
  
  // A constructor
  StickMan() {
    man = createShape(GROUP);
    ellipseMode(RADIUS);
    head = createShape(ELLIPSE, -12, 0, 24, 24);
    body = createShape(LINE, 0, 24, 0, 70);
    
    
    lHand = createShape();
    lHand.beginShape(LINES);
    lHand.vertex(hOx,hOy);
    lHand.vertex( hOx + handLen*cos(radians(lHa)), hOy + handLen*sin(radians(lHa)));
    lHand.endShape();
    
    rHand = createShape();
    rHand.beginShape(LINES);
    rHand.vertex(hOx,hOy);
    rHand.vertex(hOx + handLen*cos(radians(rHa)), hOy + handLen*sin(radians(rHa)));
    rHand.endShape();

   
    
    //rLeg = createShape(LINE, lOx, lOy, lOx + legLen*cos(radians(lLa)), lOy + legLen*sin(radians(lLa)));
    rLeg = createShape();
    rLeg.beginShape(LINES);
    rLeg.vertex(lOx,lOy);
    rLeg.vertex( lOx + legLen*cos(radians(rLa)), lOy + legLen*sin(radians(rLa)));
    rLeg.endShape();
    
    lLeg = createShape();
    lLeg.beginShape(LINES);
    lLeg.vertex(lOx,lOy);
    lLeg.vertex(lOx + legLen*cos(radians(lLa)), lOy + legLen*sin(radians(lLa)));
    lLeg.endShape();
    man_height = lOy + legLen*sin(radians(lLa));

    //lLeg = createShape(LINE, lOx, lOy, lOx + legLen*cos(radians(rLa)), lOy + legLen*sin(radians(rLa)));
    man.addChild(head);
    man.addChild(body);
    man.addChild(lHand);
    man.addChild(rHand);
    man.addChild(lLeg);
    man.addChild(rLeg);
    running = 0;
  }
  // Some functions
  
  
  public void rotateHand(int lOrH){
      pushMatrix();
      translate(curr_loc.x, curr_loc.y);
      if(lOrH ==0) {  
        lHa += handRotVel.x;
        //println(lHa,lHLim,handRotVel.x);
        if((lHa <= lHLim.x) || (lHa >= lHLim.y)){
          handRotVel.x = -1 *handRotVel.x; 
          lHa =  (lHa <= lHLim.x) ? lHLim.x : lHLim.y;
          //println("FUCK",lHa,lHLim,handRotVel.x); 
        }
        v.x = hOx + handLen*cos(radians(lHa));
        v.y = hOy + handLen*sin(radians(lHa));      
        lHand.setVertex(1,v);
      } else {
        rHa += handRotVel.y;
        if((rHa <= rHLim.x) || (rHa >= rHLim.y)){
          handRotVel.y = -1 *handRotVel.y;
          rHa =  (rHa <= rHLim.x) ? rHLim.x : rHLim.y;
        }
        v.x = hOx + handLen*cos(radians(rHa));
        v.y = hOy + handLen*sin(radians(rHa));      
        rHand.setVertex(1,v);      
      }      
      //println(lHand.getVertex(0),lHand.getVertex(1));
      popMatrix();
  }
  
  public void rotateLeg(int lOrH){
      pushMatrix();
      translate(curr_loc.x, curr_loc.y);
      if(lOrH ==0) {  
        lLa += legRotVel.x;
        if((lLa <= lLLim.x) || (lLa >= lLLim.y)){
          legRotVel.x = -1 *legRotVel.x;
          lLa =  (lLa <= lLLim.x) ? lLLim.x : lLLim.y;    
        }
        v.x = lOx + legLen*cos(radians(lLa));
        v.y = lOy + legLen*sin(radians(lLa));      
        lLeg.setVertex(1,v);
      } else {
        rLa += legRotVel.y;
        if((rLa <= rLLim.x) || (rLa >= rLLim.y)){
          legRotVel.y = -1 *legRotVel.y;
          rLa =  (rLa <= rLLim.x) ? rLLim.x : rLLim.y;
        }
        v.x = lOx + legLen*cos(radians(rLa));
        v.y = lOy + legLen*sin(radians(rLa));      
        rLeg.setVertex(1,v);      
      }      
      //println(lHand.getVertex(0),lHand.getVertex(1));
      popMatrix();
  }
  
  public void set_run_params(float speedx,float speedy,int dir, int stopx, int stopy){
    if(dir == 1){
      lHa = 65;rHa = 65;
      lLa = 80;rLa = 80;
      lHLim.x = -20;lHLim.y = 150;
      rHLim.x = -20;rHLim.y = 150;  
      lLLim.x = 30; lLLim.y = 130;
      rLLim.x = 30; rLLim.y = 130;
      handRotVel.x = -4;handRotVel.y = 4;
      legRotVel.x = -6;legRotVel.y = 6;
      curr_theta = 20;       
    }else{
      lHa = 145;rHa = 145;
      lLa = 120;rLa = 120;
      lHLim.x = 50;lHLim.y = 220;
      rHLim.x = 50;rHLim.y = 220;  
      lLLim.x = 70; lLLim.y = 170;
      rLLim.x = 70; rLLim.y = 170;
      handRotVel.x = -4;handRotVel.y = 4;
      legRotVel.x = -6;legRotVel.y = 6;
      curr_theta = -20; 
    }
      vel.x = speedx;
      vel.y = speedy;
      running = 1;
      run_targ.x = stopx;
      run_targ.y = stopy;         
  }
  
  public void resetMan(){
    lHa = 135;rHa = 45;
    lLa = 135;rLa = 45;
    curr_theta = 0;  
    vel.set(0,0,0);
  
    handRotVel.set(0,0);
    legRotVel.set(0,0);

    lHLim.set(95,210);
    rHLim.set(-30,85);  
    lLLim.set(95,210);
    rLLim.set(-30,85);  
  }
  
  public void update_locs(){
    
    rotateHand(0);
    rotateHand(1);
    rotateLeg(1);
    rotateLeg(0);
    curr_loc.x += vel.x;
    curr_loc.y += vel.y;
    
    if(curr_loc.dist(run_targ) < vel.mag()){
      running = 0;
      resetMan();   
    }
    
  }
// Display the shape!
  public void display() {
    pushMatrix(); 
    update_locs();
    translate(curr_loc.x,curr_loc.y);    
    rotate(radians(curr_theta));
    shape(man);    
    popMatrix();
  }  

  
}

StickMan man1;
StickMan man2;
/*
 * Processing's setup method.
 *
 * Do all your one-time setup routines in here.
 */
public void setup() {
  // Set up the drawing area size and renderer (P2D / P3D).
  size(CANVAS_WIDTH, CANVAS_HEIGHT, P2D);
  frameRate(60);

  // Parameters: 
  // q- PApplet
  // - soundtrack filename (relative to sketch's folder)
  // - beats per minute in the song
  // - how many rows in Rocket correspond to one beat
  moonlander = Moonlander.initWithSoundtrack(this, "MetalVersion.mp3", 128, 4);

  // Last thing in setup; start Moonlander. This either
  // connects to Rocket (development mode) or loads data 
  // from 'syncdata.rocket' (player mode).
  // Also, in player mode the music playback starts immediately.
  moonlander.start();
  
  man1 = new StickMan();
  man2 = new StickMan();
  man1.curr_loc.set(width+20,ground_y-man1.man_height);man1.curr_theta = 0;
  man2.curr_loc.set(-20,ground_y-man1.man_height);man2.curr_theta = 0;
}

/*
 * Processing's drawing method
 */
 
 //Globals for bg color
int bg_red = 135; 
int bg_green = 206;
int bg_blue = 250;

int renderTree2 = 1;
int renderFire = 0;
int renderFire2 = 0;
int renderRain = 0;
int currSceneStage = 0;
int prevTimeMarker = 0;
int renderLightning = 0;

PVector tree1Loc = new PVector(100,ground_y,0);
PVector tree2Loc = new PVector(CANVAS_WIDTH/2,ground_y,0);
PVector fireLoc = new PVector(CANVAS_WIDTH/2,ground_y,0.6f);
PVector cloudLoc = new PVector(200,100,0.5f);
int woodColor = color(139,69,19,255);
int rain_g = ground_y;
int lightningCount = 0;

public void sceneScheduler(){
  if(currSceneStage == 0){
      man1.set_run_params(-1,0,0,width/2+20,(int)(ground_y-man1.man_height));
      man2.set_run_params(1,0,1,width/2 - 20,(int)(ground_y-man2.man_height));
      currSceneStage +=1;
      println("INITIALIzed",currSceneStage);      
  }
  if ((currSceneStage == 1) && (man1.running == 0) && (man2.running == 0)){
    // Initiate cutting sequence
      prevTimeMarker = millis();
      man2.lHa = 0;man2.rHa = 0;      
      man2.lHLim.set(-90,90);        
      man2.handRotVel.set(10,0);
      man1.lHa = 180;man1.rHa = 180;      
      man1.rHLim.set(90,270);        
      man1.handRotVel.set(0,10);
      currSceneStage += 1;
  }
  
  if ((currSceneStage == 2)&& ((millis() - prevTimeMarker) > 6000)) {
     man2.resetMan();man1.resetMan();
     float rot = (millis() - prevTimeMarker - 6000) * 0.05f;
     if(rot <=90)
       tree2Loc.z = radians(rot);
     if(rot >=150)
     {
       currSceneStage += 1;
       renderTree2 = 0;
     }
  }
  
  if (currSceneStage == 3){
     //Make them sit and render fire!     
     //Should Draw firewood here
      man1.curr_loc.set(width/2+90,ground_y-70); man1.curr_theta = 20;
      man1.lLa = 135; man1.rLa = 120;
      man1.rLLim.set(90,120);
      man2.curr_loc.set(width/2-90,ground_y-70); man2.curr_theta = -20;
      man2.lLa = 60; man2.rLa = 45;
      man2.lLLim.set(0,90);
      renderFire = 1;
      fireLoc.z = 0.9f;
      currSceneStage += 1;
      prevTimeMarker = millis();
  }
  if ((currSceneStage == 4)&& ((millis() - prevTimeMarker) > 7500)){
    //Initiate Rain and move cloud towards fire
      renderRain = 1;
      cloudLoc.x += 1; 
      if(cloudLoc.x >= (width/2-100)){
        cloudLoc.x = width/2-100;
        prevTimeMarker = millis();
        currSceneStage += 1;
      }
  }
  
  if (currSceneStage == 5) {
    //Initiate Rain and move cloud towards fire
      renderRain = 1;
      fireLoc.z -= 0.01f;
      if(fireLoc.z <= 0.1f)
        fireLoc.z = 0.1f;      
      if ((millis() - prevTimeMarker) > 2000){
        renderFire = 2;      
        woodColor = color(0,0,0,255);
        if((millis() - prevTimeMarker) > 3000){
          //Wake the guys up!
          man1.curr_loc.set(width/2+90,ground_y-man1.man_height); man1.resetMan();
          man2.curr_loc.set(width/2-90,ground_y-man2.man_height); man2.resetMan();
          
        }
        if((millis() - prevTimeMarker) > 4500){
          currSceneStage += 1;
          man1.set_run_params(-2,0,0,100-20,(int)(ground_y-man1.man_height));
          man2.set_run_params(-1,0,0,100+20,(int)(ground_y-man2.man_height));
        }
      }
  }
  if (currSceneStage == 6){
      cloudLoc.x -= 1; 
      if(cloudLoc.x <= 20){
         cloudLoc.x = 20; 
         prevTimeMarker = millis();
         currSceneStage += 1;    
      }
      if(cloudLoc.x <= 60){
        rain_g = ground_y-200;
      }
  }
  
  if((currSceneStage ==7)&&(millis() - prevTimeMarker)>2000){
      //Trigger lightning attack now!
      cloudLoc.x += 1;
      if(cloudLoc.x >=60){
        rain_g = ground_y;
      }
      if(cloudLoc.x >=130){
        cloudLoc.x = 130;
        cloudLoc.y += 1;
        if(cloudLoc.y >=(ground_y-300)){
          cloudLoc.y = ground_y-300;
          currSceneStage += 1;
          prevTimeMarker = millis();
        }
      }
  }
 
  if((currSceneStage ==8)&&(millis() - prevTimeMarker)>500){
      //Trigger lightning attack now!
      prevTimeMarker = millis();
      renderLightning = (1 - renderLightning); 
      lightningCount += renderLightning;
      
      if(lightningCount > 2){
        renderRain = 0;
        prevTimeMarker = millis();
        currSceneStage +=1;
        man1.set_run_params(1.5f,0,1,width+30,(int)(ground_y-man1.man_height));
        man2.set_run_params(1.5f,0,1,width+30,(int)(ground_y-man2.man_height));
      }
  }
   if(currSceneStage ==9){
     if((millis() - prevTimeMarker)>400){
       prevTimeMarker = millis();
       renderLightning = (1 - renderLightning);
       lightningCount += renderLightning;
     }
     if(lightningCount > 8){
       renderFire2 = 1;
       fireLoc.z = 0.5f;
     }
     cloudLoc.x+=1;
     cloudLoc.y-=0.5f;
     if (cloudLoc.y <= 10)
       cloudLoc.y = 10;
     if(man1.running == 0){
       renderLightning = 0;
       currSceneStage += 1;
     } 
   }
  
}

public void draw() {
  // Draw something
  // Get values from Rocket using 
  moonlander.update();
  int initialize_seq = moonlander.getIntValue("playback_start");
  
  sceneScheduler();
  //Render the Constants
  background(bg_red, bg_green,bg_blue);  
  ground();
  tree(tree1Loc);
  
  if(renderTree2==1)
   tree(tree2Loc);
  
  // TO render fire
  if(renderFire > 0){
    pushMatrix();
    translate(width/2,ground_y-30);
    rectMode(CORNER);
    rotate(radians(30));
    for(int i=0;i<7;i++){
      rotate(radians(15));
      fill(woodColor);
      rect(0,0,50,10);    
    }
    popMatrix();
    fireLoc.y = ground_y-20;
    
    if(renderFire == 1)
      fire(fireLoc);
  }  
  
  man1.display();
  man2.display();
  draw_cloud(cloudLoc);
  if (renderLightning ==1){ 
    draw_lightning(cloudLoc,man2.curr_loc);
    draw_lightning(cloudLoc,man1.curr_loc);
  }  
  if(renderRain==1){
    let_it_rain(cloudLoc,rain_g);    
  }
  if(renderFire2 == 1){    
    fireLoc.x = man1.curr_loc.x; fireLoc.y = man1.curr_loc.y;
    fire(fireLoc);
    fireLoc.x = man2.curr_loc.x; fireLoc.y = man2.curr_loc.y;
    fire(fireLoc);
  }
}

/*
 * No playback controls as in Processing_Demobase;
 * Rocket controls playing
 */

  static public void main(String[] passedArgs) {
    String[] appletArgs = new String[] { "--full-screen", "--bgcolor=#666666", "--hide-stop", "stickman" };
    if (passedArgs != null) {
      PApplet.main(concat(appletArgs, passedArgs));
    } else {
      PApplet.main(appletArgs);
    }
  }
}
