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

import moonlander.library.*; 
import ddf.minim.spi.*; 
import ddf.minim.signals.*; 
import ddf.minim.*; 
import ddf.minim.analysis.*; 
import ddf.minim.ugens.*; 
import ddf.minim.effects.*; 

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 demo extends PApplet {

PShader tunneli;

/* 
 * A simple example of how to use
 * Processing as a demo platform.
 * 
 * Features:
 *  - Setup for rendering resolution-independent
 *    2D graphics
 *  - Music playback with Minim
 *  - Simple play/pause/seek-functionality
 */
 

 
// Minim is needed for the music playback.







// 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 = 1920; //960;
int CANVAS_HEIGHT = 1080; //720;

// You can skip backwards/forwards in your demo by using the 
// arrow keys; this controls how many milliseconds you skip
// with one keypress.
int SONG_SKIP_MILLISECONDS = 2000;

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

// Needed for audio
Minim minim;
AudioPlayer song;

Moonlander moonlander;

PImage ninja;
PImage taru;
PImage miranda;

PShader gre;
PImage tex;

/*
 * Sets up audio playing: call this last in setup()
 * so that the song doesn't start to play too early.
 *
 * By default, playback doesn't start automatically \u2013
 * you must press spacebar to start it.
 */
public void setupAudio() {
  minim = new Minim(this);
  song = minim.loadFile("They're Out There.mp3");
  // Uncomment this if you want the demo to start instantly
  song.play();
}

/*
 * Processing's setup method.
 *
 * Do all your one-time setup routines in here.
 */
public void setup() {
  moonlander = Moonlander.initWithSoundtrack(this, "They're Out There.mp3", 127, 8);
  
  // Set up the drawing area size and renderer (usually P2D or P3D,
  // respectively for accelerated 2D/3D graphics).
  size(CANVAS_WIDTH, CANVAS_HEIGHT, P3D);

  // Drawing options that don't change, modify as you wish
  noStroke();
  frameRate(30);
  fill(255);
  smooth();
  
  rectMode(CENTER);
  ninja = loadImage("sydanNinja.png");
  taru = loadImage("sydanTaru.png");
  miranda = loadImage("sydanMiranda.png");
  textureMode(NORMAL);
  
  tunneli = loadShader("tunneli.glsl"); 
  tunneli.set("iResolution", (float)width, (float)height);
  
  
  gre = loadShader("gre.glsl"); 
  tex = loadImage("data/graffathon.png");
  
  gre.set("iResolution", (float)width, (float)height);
  gre.set("iChannel0", tex);

  setupAudio(); // t\u00e4m\u00e4 vikaksi! jotta musa pysyy synkassa
  
  moonlander.start();
}

public void effect1(int time) {
  pushMatrix();
  
  background(0);
  
  stroke(60, 0, 100);
  
  float distance = 0.5f;
   
  drawCircles(time, 0.0f, 0.001f, (cos(time*0.001f))*0.2f);
  
  popMatrix();
}

public void effect2(int time) {
  pushMatrix();
  
  background(0);
  
  //float distance = 0.5;//(float) moonlander.getValue("distance");

  scale(0.5f);

  noStroke();
  
  drawCircles(time, 0.4f*sin(time*0.001f), 0.001f, 0.00f);
  drawCircles(time, 0.8f*sin(time*0.001f), 0.0012f, 0.05f);
  drawCircles(time, 1.2f*sin(time*0.001f), 0.0013f, 0.07f);

  
  popMatrix();
}

public void effect3(int time) {
  pushMatrix();
  
  stroke(60, 0, 100);
  
  //float distance = 0.5;//(float) moonlander.getValue("distance");

  scale(0.5f);

  drawCircles(time, 0.4f*sin(time*0.001f), 0.001f, 0.00f);
  
  popMatrix();
}

public void konnakursio(int time, int level, int shards) {
   if (level <= 0) {
    return;
   } 
   
   pushMatrix();
     
   scale(0.7f);
   
   float rot = 10*sin(time * 0.001f);
   rotateX(radians(200.0f + rot));
   rotateY(radians(50.0f + rot));
   
   
   fill(255, 0, (level+1)*30*(1.5f+sin(time*0.001f)));

   noStroke();
   konna();
   // sphere(350);
   
   for (int i=0; i < shards; i++) {
     rotateZ(1);
     rotateX(1);

     translate(150*time*0.0001f, 0, 0);
     konnakursio(time, level - 1, shards);
   }
   
   popMatrix();
}

public void effectKonnaKursio(int time) {
  resetMatrix();
  background(0);
  
  float secs = time / 1000.0f;

  imageMode(CENTER);
  sphereDetail(10);
  
  pushMatrix();
  
  translate(0, 150, -800.0f);
  
  konnakursio(time, 8,3);
  
  popMatrix();
}

public void konna() {

  scale(1.0f);
  sphere(100);
}


public void konna2() {
  scale(1.0f);
  box(20.0f, 120.0f, 20.0f);
}

public void effectKonnaKursio2(int time) {
  resetMatrix();
  background(0);
  
  float secs = time / 1000.0f;
  
  pushMatrix();
  
  translate(0, -height/2.0f - 100, -800.0f);
  
  konnakursio2(time, 30, 1);
  
  translate (-400, -200, -400);
  
  konnakursio2(time, 30, 1);
  
  translate (800, 0, 0);
  
  konnakursio2(time, 30, 1);
  
  popMatrix();
}

public void konnakursio2(int time, int level, int shards) {
   if (level <= 0) {
    return;
   }
   
   if (time > 20000) {
     translate(100, 300, -100);
     fill(100, 255, 150+(level+1)*10*sin(time*0.001f));
   }
   else {
     fill(200+(level+1)*10*sin(time*0.001f), 255, 50+(level+1)*10);
   }
   
   pushMatrix();
     
   scale(1.0f);

   noStroke();
   konna2();
   
   for (int i=0; i < shards; i++) {
     if (time > 20000) {
       rotateZ(1);
     }
     rotateY(radians(30)*sin(time*0.001f));

     translate(60*time*0.0001f, 40, 0);
     konnakursio2(time, level - 1, shards);
   }
   
   popMatrix();
}

public void texturedBox(float dx, float dy, float dz, int i){
  dx*=0.5f;
  dy*=0.5f;
  dz*=0.5f;
  noStroke();
  beginShape();
  if(i == 0) {
    texture(ninja);
  }
  else if (i == 1) {
    texture(taru);
  }
  else if (i == 2) {
    texture(miranda);
  }
  
  vertex( dx,  dy, dz, 1.f, 1.f);
  vertex( dx, -dy, dz, 1.f, 0.f);
  vertex(-dx, -dy, dz, 0.f, 0.f);
  vertex(-dx,  dy, dz, 0.f, 1.f);
  endShape();


}

public void sydamia(){
  scale(ASPECT_RATIO/(CANVAS_WIDTH/2.0f), -2.0f/CANVAS_HEIGHT);
  scale(0.5f);
  
  for(int i=0; i<3; i++){
   pushMatrix();
   //translate(width/2, height/2, 0);
   translate(-150, -50, 0);
   translate(i*150, 0, 0);

      
    rotateY(millis()*0.005f);
    noStroke();
    //fill(200, 30, 100);
    //texture(sydan);
    texturedBox(50, 100, 10, i);
    
    popMatrix();
  }
}

public void kuutioita(){
  shader(tunneli);
  
  scale(ASPECT_RATIO/(CANVAS_WIDTH/2.0f), -2.0f/CANVAS_HEIGHT);
  scale(0.5f);
  
  for(int j=0; j<10; j++){
    for(int i=0; i<5; i++){
     pushMatrix();
     //translate(width/2, height/2, 0);
     translate(-200, 0, 0);
     translate(i*100, 0, -j*400);
     if(i==1 || i==3)
       translate(0, 100, 0);
     else
        translate(0, -100, 0);
      tunneli.set("iGlobalTime", (float)millis()*0.001f);  
      rotateY(millis()*0.005f);
      rotateZ(millis()*0.0001f);
      noStroke();
      fill(200, 30, 100);
      //texture(sydan);
      box(100, 100, 100);
      
      popMatrix();
    }
  }
}

public void greets(int time) {
  shader(gre);
  gre.set("iGlobalTime", (float)time*0.001f);
  
  scale(ASPECT_RATIO/(CANVAS_WIDTH/2.0f), -2.0f/CANVAS_HEIGHT);
  scale(0.5f);
  
  pushMatrix();
  fill(0, 30, 100);
  translate(-200, 200, 0);
  box(1920, 1080, 30);
  popMatrix();
}

/*
 * Your drawing code ends up in here!
 *
 */
public void drawDemo(int time) {
  if (time < 10000) {
    resetShader();
    effect1(time);
  }
  else if (time < 19500) {
    resetShader();
    effect3(time - 10000);
  }
  else if (time < 30000) {
    resetShader();
    effect2(time - 19500);
  }
  else if (time < 60000) {
    resetShader();
    effectKonnaKursio(time - 30000);
  }
  else if (time < 100000) {
    resetShader();
    effectKonnaKursio2(time - 60000);
  }
  else if (time < 110000) {
    resetShader();
    sydamia();
  }
  else if (time < 120000) {
    resetShader();
    kuutioita();
  }
  else {
    resetShader();
    greets(time+11000);
  }
}

public void drawCircles(float time, float distance, float speed, float size) {
  pushMatrix();
  
  for (float i = 0; i < 23; i++) {
    fill(i*20, 0, i*20);
    
    /*if (time > 5000) {
      fill(22*20 - i*20, 0, 22*20 - i*20);
    }*/
    
    ellipse(distance*sin(time*speed+i*2.0f), 
            distance*cos(time*speed+i*2.0f), 
            size+1.0f/(i+2.0f),
            size+1.0f/(i+2.0f)); // x, y, leveys, korkeus
  }
  
  popMatrix();
}

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

/*
 * Processing's drawing method \u2013 all
 * rendering should be done here!
 */
public void draw() {
  moonlander.update();
  
  // Reset all transformations.
  resetMatrix();
  

  // The following lines map coordinates so that we can
  // draw in a resolution independent coordinate system. 
  //
  // After this line coordinate axes are as follows:
  //   x: -ASPECT_RATIO ... ASPECT_RATION
  //   y: -1 ... 1  
  // Negative x is at left, y at bottom, origo at the center of 
  // the screen.
  // This is the coordinate system you're probably used to
  // already!
  //translate(CANVAS_WIDTH/2.0, CANVAS_HEIGHT/2.0); // needed in 2D mode
  translate(-50,0,-200); // needed in 3D mode
  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();

  // Draw coordinate axes for reference.
  drawAxes();
  // Draw demo at the current song position.
  drawDemo(millis());//song.position());
}

public void keyPressed() {
  if (key == CODED) {
    // Left/right arrow keys: seek song
    if (keyCode == LEFT) {
      song.skip(-SONG_SKIP_MILLISECONDS);
    } 
    else if (keyCode == RIGHT) {
      song.skip(SONG_SKIP_MILLISECONDS);
    }
  }
  // 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", "demo" };
    if (passedArgs != null) {
      PApplet.main(concat(appletArgs, passedArgs));
    } else {
      PApplet.main(appletArgs);
    }
  }
}
