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 derp 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)


// All these you can (must) change!
// These control how big the opened window is.
// Before you release your demo, set these to 
// full HD resolution (1920x1080).
int CANVAS_WIDTH = 1280;
int CANVAS_HEIGHT = 720;

//int CANVAS_WIDTH = 1920;
//int CANVAS_HEIGHT = 1080;

// Don't change this \u2013 needed for resolution independent rendering
float ASPECT_RATIO = (float)CANVAS_WIDTH/CANVAS_HEIGHT;

Moonlander moonlander;

PShape shape;
//Water water;
//Ground ground;
Planet planet;
FractalTree tree0;

public void setupAudio() {
   // Parameters: 
  // - 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, "DST-AngryRobotIII.mp3", 130, 16);

  // 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();
}

public void setup() {
  // Set up the drawing area size and renderer (usually P2D or P3D,
  // respectively for accelerated 2D/3D graphics).
  size(CANVAS_WIDTH, CANVAS_HEIGHT, P2D);

  // Drawing options that don't change, modify as you wish
  frameRate(60);
  noStroke();
  fill(255);
  smooth();
  
  // Load stuff.
  //shape = loadShape("drawing2.svg");
  
  // Setup stuff.
  float waterScale = 1.0f;
  
//  water = new Water();
//  water.scale = new PVector(waterScale, waterScale);
//  
//  ground = new Ground();
//  ground.scale = new PVector(waterScale, waterScale);

  planet = new Planet();
  planet.rot = PI;
  planet.scale = new PVector(0.75f, 0.75f);
  
  tree0 = new FractalTree();
  tree0.scale = new PVector(0.5f, 0.5f);
  tree0.branches = 6;
  tree0.maxLength = 0.5f;
  tree0.lifeLength = 0;
  tree0.lifeAngles = 0.0f;
  tree0.lifeLengthMulti = 1.0f;
  tree0.lifeAngleMulti = 1.0f;
  tree0.lengthMulti = 1.0f;
  tree0.angleMulti = 1.0f;
  tree0.angles[0] = 0.666666f * PI;
  tree0.angles[1] = 0.5f * PI;
  tree0.angles[2] = 0.333333f * PI;
    
  setupAudio();
}

class Planet
{
  Water water;
  Ground ground;
  
  PVector pos;
  PVector scale;
  float rot;
  
  Planet()
  {
    pos = new PVector(0.0f,0.0f,0.0f);
    scale = new PVector(1,1);
    rot = 0;
    
    // Create the ground and water.
    ground = new Ground();
    water = new Water();
    
    float waterScale = 1.1f;
    water.scale = new PVector(waterScale, waterScale);
    water.planetScale0 = 0.5f;
    
    ground.pos.x = 0.0f;
    ground.pos.y = -0.075f;
    
    // TODO: Create trees etc.
  }
  
  public void display(float input0)
  {
    pushMatrix();
    
    // Apply localTransform.
    translate(pos.x, pos.y);
    scale(scale.x, scale.y);
    rotate(rot);
    
    water.display(input0);
    //ground.display(input0);
    
    popMatrix();
  }
}

class FractalTree
{
  PVector pos;
  PVector scale;
  float rot;  
  
  float[] angles = new float[3];
  
  int branches = 6;        // no of brances.
  float maxAngle = PI * 0.333f;
  float maxLength = 0.5f;
  float lifeLength = 0.0f; // Timer for growing branches.
  float lifeAngles = 0.0f;   // Timer for opening the angles.
  
  float lifeLengthMulti = 1.0f;
  float lifeAngleMulti = 1.0f;
  
  float lengthMulti = 1.0f;  // How much of the branches' length is carried over to the children.
  float angleMulti = 1.0f;   // How much of the branches' angle is carried over to the children.
    
  FractalTree()
  {
    pos = new PVector(0.0f,0.0f);
    scale = new PVector(1,1);
    rot = 0;    
    
    angles[0] = 0.666666f * PI;
    angles[1] = 0.5f * PI;
    angles[2] = 0.333333f * PI;
  }
  
  public void display()
  {
    pushMatrix();
    
    // Apply localTransform.
    translate(pos.x, pos.y);
    scale(scale.x, scale.y);
    rotate(rot);
    
    stroke(255);
    //line(0,0,0,0,-1,0);
    strokeWeight(0.01f);
    branch(0, lifeLength, lifeAngles, maxLength, maxAngle);
    //line(0,0,0,1);
  
    
    popMatrix();    
  }
  
  public void branch(int branchNo, float curLifeLength, float curLifeAngle, float curMaxLength, float curMaxAngle)
  {
    // TODO: Fix this!
    if(branchNo < branches)
    {
      pushMatrix();      
      float angle = curLifeAngle * curMaxAngle;
      rotate(min(angle, curMaxAngle));
      float len = curLifeLength * curMaxLength;
      line(0,0,0, 0, min(len, curMaxLength), 0);
      translate(0, min(len, curMaxLength));
      branch(branchNo+1, curLifeLength * lifeLengthMulti, curLifeAngle * lifeAngleMulti,
      curMaxLength*lengthMulti, curMaxAngle*angleMulti);
      popMatrix();
      
      pushMatrix();      
      rotate(-min(angle, curMaxAngle));
      line(0,0,0, 0, min(len, curMaxLength), 0);
      translate(0, min(len, curMaxLength));
      branch(branchNo+1, curLifeLength * lifeLengthMulti, curLifeAngle * lifeAngleMulti,
      curMaxLength*lengthMulti, curMaxAngle*angleMulti);
      popMatrix();
    }
  }
  
  public void branch2(int branchNo, float curLifeLength, float curLifeAngle, float curMaxLength, float curMaxAngle)
  {
    // TODO: Fix this!
    if(branchNo < branches)
    {
      pushMatrix();      
      float angle = curLifeAngle * curMaxAngle;
      rotate(min(angle, curMaxAngle));
      float len = curLifeLength * curMaxLength;
      line(0,0,0, 0, min(len, curMaxLength), 0);
      translate(0, min(len, curMaxLength));
      branch2(branchNo+1, curLifeLength - lifeLengthMulti, curLifeAngle - lifeAngleMulti,
      curMaxLength*lengthMulti, curMaxAngle*angleMulti);
      popMatrix();
      
      pushMatrix();      
      rotate(-min(angle, curMaxAngle));
      line(0,0,0, 0, min(len, curMaxLength), 0);
      translate(0, min(len, curMaxLength));
      branch2(branchNo+1, curLifeLength - lifeLengthMulti, curLifeAngle - lifeAngleMulti,
      curMaxLength*lengthMulti, curMaxAngle*angleMulti);
      popMatrix();
    }
  }
}

class Water
{
  int waterColor = color(50, 50, 255);
  float yoff = 0.0f;
  float planetScale0 = 0.85f;
  float planetScale1 = 1;
  int points = 100;
  int smoothing = 50;
  
  PVector pos;
  PVector scale;
  float rot;
  
  Water()
  {
    pos = new PVector(0.0f,0.0f,0.0f);
    scale = new PVector(1,1);
    rot = 0;
  }
  
  public void display(float input0)
  {
    pushMatrix();
    
    // Apply localTransform.
    translate(pos.x, pos.y);
    scale(scale.x, scale.y);
    rotate(rot);
    
    noStroke();
    beginShape();
    
    float xoff = 0.0f;
    for (float x = 0; x < points-1-smoothing; x++) {
      // Calculate a y value according to noise, map to 
      float y = map(noise(xoff, yoff), 0, 1, planetScale0, planetScale1); // Option #1: 2D Noise
      
      // Set the vertex
      float vertX = x * PI * 2.0f / points;
      fill(50, 50, 255-(y) * (sin(vertX)+1.0f)*0.5f);
      vertex((y) * cos(vertX), (y) * sin(vertX)); 
      // Increment x dimension for noise
      xoff += 0.05f;
    }
    for(float x = points-1-smoothing; x <= points-1; x++)
    {
      float y0 = map(noise(xoff, yoff), 0, 1, planetScale0, planetScale1); // Option #1: 2D Noise
      float y1 = map(noise((x-points)*0.05f, yoff), 0, 1, planetScale0, planetScale1);
      float y = map(x, points-1-smoothing, points-1, y0, y1);
      //map(noise(xoff, yoff), 0, 1, planetScale0, planetScale1); // Option #1: 2D Noise
      
      // Set the vertex
      float vertX = x * PI * 2.0f / points;
      fill(50, 50, 255-(y) * (sin(vertX)+1.0f)*0.5f);
      vertex((y) * cos(vertX), (y) * sin(vertX)); 
      // Increment x dimension for noise
      xoff += 0.05f;      
    }
    
    yoff += 0.01f;
    endShape(CLOSE);
    
    popMatrix();
  }
}

class Ground
{
  PVector pos;
  PVector scale;
  float rot;
    
  Ground()
  {
    pos = new PVector(0.0f,0.0f,0.0f);
    scale = new PVector(1,1);
    rot = 0;
  }
  
  public void display(float input0)
  {
    pushMatrix();
    
    // Apply localTransform.
    translate(pos.x, pos.y);
    scale(scale.x, scale.y);
    rotate(rot);
    
    // TODO: Get the shape's vertices instead.
    //float timer = 150*2.5;//(millis() % 1000);
    fill(0, 255, 0);
    shape(shape, 0, 0, scale.x*2, scale.y*2);
    
    popMatrix();
  }
}

/*
 * Your drawing code ends up in here!
 *
 */
public void drawDemo(int time) {  
  // Draw centered unit circle
  ellipse(0.f + (time%100)*0.01f, 0.f, 1.0f, 1.0f);  
}

/*
 * Draws coordinate axes (for reference).
 * You can remove this method if you don't 
 * need to see the axes.
 */
public void drawAxes() {
  // Drawing options for axes
  stroke(255);
  strokeWeight(0.004f);
  fill(255);

  // X-axis
  line(-ASPECT_RATIO, 0, ASPECT_RATIO, 0); 
  pushMatrix();
  resetMatrix();
  text(String.format("%.3f", -ASPECT_RATIO), 12, CANVAS_HEIGHT/2);
  text(String.format("%.3f", ASPECT_RATIO), CANVAS_WIDTH-42, CANVAS_HEIGHT/2);
  popMatrix();
  
  // Y-axis
  line(0, -1, 0, 1);
  pushMatrix();
  resetMatrix();
  text("1", CANVAS_WIDTH/2+12, 12);
  text("-1", CANVAS_WIDTH/2+12, CANVAS_HEIGHT - 12);
  popMatrix();
}

public float easeinoutQuad(float a, float b, float t)
{
  t *= 2;
  if(t < 1)
    return 0.5f*b*t*t+a;
  return -0.5f*b * ((t-2)*(t-1)-1)+a;
}

/*
 * Processing's drawing method \u2013 all
 * rendering should be done here!
 */
 
 
 int phase = 0;
 int prevMillis = 0;
 float t0 = 10000.0f;
 //bool delay = false;
 int delay = 0;
 float delayTime = 50000.0f; // ms
 
 int mode = 0;
 
public void draw() {
  if(mode == -1)
  {
    //moonlander.end();
    clear();
   
    resetMatrix();
    //translate(0.1*CANVAS_WIDTH/2.0, 0.1*CANVAS_HEIGHT/2.0);
    //scale(0.1*CANVAS_WIDTH/2.0/ASPECT_RATIO, -0.1*CANVAS_HEIGHT/2.0);
    
    fill(255);
    textSize(30);
    text("Greetings guys, see you at Assembly!", 0,30); // Write "LAX" at coordinate (0,40)
    text("-Kajak Crew", 0,70);
    
    text("Music: AngryRobotIII - Deceased Superior Technician", 0, 150);
    text("Attribution 3.0 Unported (CC BY 3.0)", 0, 190);
    
    return;
  }
  
  moonlander.update();
  
  // Reset all transformations.
  resetMatrix();

  // Switch to -ASPECT_RATIO, ASPECT_RATIO; -1, 1 range.
  translate(CANVAS_WIDTH/2.0f, CANVAS_HEIGHT/2.0f);
  scale(CANVAS_WIDTH/2.0f/ASPECT_RATIO, -CANVAS_HEIGHT/2.0f);
    
  // Clear the screen after previous frame.
  // If you comment this line, you always draw on top the last frame,
  // which can lead to something interesting.
  clear();
  
  mode = moonlander.getIntValue("mode");

  // Draw coordinate axes for reference.
  //drawAxes();
  // Draw demo at the current song position.
  //drawDemo(moonlander.getIntValue("track_name"));

  
  // Tree.
  
  /*if(millis() > prevMillis+t0*2)
  {
    prevMillis += t0*2;
    phase = (phase + 1) %3;
    print("switch\n");
  } */
  
  //float t = sin(PI*millis()/t0)* 10;
  //float ta = millis()-prevMillis;
  //if(ta > t0)
  //{
  //  prevMillis += t0;
  //  ta = 0;
  //  phase = (phase+1)%3;
  //  print("swap");
  //  
  //  //delay = delayTime;
  //}
  
  // Tree0 animation.
  float ta = (float)moonlander.getValue("ta");
  phase = moonlander.getIntValue("phase");
  float growth = (float)moonlander.getValue("growth");
  
  if(growth < 1.0f)
  {
    //tree0.maxAngle = map(ta, 0, 1.0, tree0.angles[phase], tree0.angles[(phase+1)%3]);
    //float scale = map(ta, 0, 1.0, (phase)*0.25+1, ((phase+1)%3)*0.25+1);
    //tree0.scale = new PVector(scale, scale);
    
    tree0.maxAngle = tree0.angles[(phase+1)%3];
    float scale = ((3-phase+1)%3)*0.5f+1;
    tree0.scale = new PVector(scale, scale);
    tree0.lifeLength = growth;
    tree0.lifeAngles = growth;
  }
  else
  {
    tree0.maxAngle = map(ta, 0, 1.0f, tree0.angles[phase], tree0.angles[(phase+1)%3]);
    float scale = map(ta, 0, 1.0f, (2-phase)*0.5f+1, ((3-phase+1)%3)*0.5f+1);
    tree0.scale = new PVector(scale, scale);
    ta = abs(2.0f*(ta-0.5f))*0.5f+0.5f;
    tree0.lifeLength = 1.0f;//ta;
    tree0.lifeAngles = 1.0f;//ta;    
  }
  tree0.display();
  
    
  if(mode == 1 || mode == -1)
  {
    float waterAssist = (float)moonlander.getValue("waterAssist");
    
    if(waterAssist < 1.0f)
    {
      planet.water.planetScale0 = waterAssist*(float)moonlander.getValue("scale0");
      planet.water.planetScale1 = waterAssist*(float)moonlander.getValue("scale1");
    }
    else
    {
      planet.water.planetScale0 = (float)moonlander.getValue("scale0");
      planet.water.planetScale1 = (float)moonlander.getValue("scale1");
    }
    planet.display(0);
  }
  
  //ta = (ta%t0) / t0;
  //tree0.lifeLength = 0*ta;
  //tree0.lifeAngles = 0*ta;
  
  //print(ta);
  // CONTINUE!
  //t = millis()/t0
  
  //tree0.lifeLength = abs(sin(PI*millis()/t0)* 10);
  //tree0.lifeAngles = abs(sin(PI*millis()/t0)* 10);
    
  //print(t + "\n");
  
//  tree0.maxAngle = map(ta, 0, 1.0, tree0.angles[phase], tree0.angles[(phase+1)%3]);
//  float scale = map(ta, 0, 1.0, (phase)*0.25+1, ((phase+1)%3)*0.25+1);
//  tree0.scale = new PVector(scale, scale);
//  
//  //print(" " + ta + " \n");
//  
//  //tree0.maxAngle = map(t, -10, 10.0, tree0.angles[phase], tree0.angles[(phase+1)%3]);
//  //float scale = map(t, -10, 10.0, (2-phase)*0.25+1, ((5-(phase+1))%3)*0.25+1);
//  //tree0.scale = new PVector(scale, scale);
//  
//  ta = abs(2.0*(ta-0.5));
//  //float tb = (millis() % t0) / t0;
//  //tb = (0.5-tb)*2.0;
//  //tb = abs(2*(tb-0.5));
//  //print(" " + tb + " \n" );
//  tree0.lifeLength = ta;
//  tree0.lifeAngles = ta;
  //tree0.lifeLength = abs(sin(PI*millis()/t0)* 10);
  //tree0.lifeAngles = abs(sin(PI*millis()/t0)* 10);
  
  
  //water.display(0);
  //ground.display(0);
}

public void keyPressed() {
  if (key == CODED) {
    // Left/right arrow keys: seek song
    if (keyCode == LEFT) {
      planet.water.scale = new PVector(0.1f + planet.water.scale.x, 0.1f + planet.water.scale.y);
    } 
    else if (keyCode == RIGHT) {
      planet.water.scale = new PVector(0.1f - planet.water.scale.x, 0.1f - planet.water.scale.y);
    }
  }
  // Space bar: play/payse
  else if (key == ' ') {
//    if (song.isPlaying())
//      song.pause();
//    else
//      song.play();
  }
  // Enter: spit out the current position
  // (for syncing)
  else if (key == ENTER) {
    //print(song.position() + ", ");
  }
}

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