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




Moonlander moonlander;
PShader shader;

final int bpm = 140;
final int rows_per_beat = 4;

// offset of Dub Zap song to the beat
// calculated in audacity
final float song_offset_seconds = -0.117f;

PImage floor_texture;

PImage intro_header;
PImage intro_createdby;
PImage intro_wno;
PImage intro_qazhax;
PImage intro_locketeerian;
PImage intro_music;
PImage fake_scene;

public void setup() {
    //size(720, 480, P3D);
    
  moonlander = Moonlander.initWithSoundtrack(this,
        "Gunnar_Olsen-Dub_Zap_fadeout.wav", bpm, rows_per_beat);
    // ENABLE THESE FOR FINAL RELEASE
    
    noCursor();

    floor_texture = loadImage("floor.png");
    intro_header = loadImage("title_header.png");
    intro_createdby = loadImage("title_createdby.png");
    intro_wno = loadImage("title_wno.png");
    intro_qazhax = loadImage("title_qazhax.png");
    intro_locketeerian = loadImage("title_locketeerian.png");
    intro_music = loadImage("title_music.png");
    fake_scene = loadImage("fakeshadertex.png");

    frameRate(60);


    shader = loadShader("waves.glsl","wavevert.glsl");
    // Set shadertoy uniforms
    shader.set("iResolution", (float)width, (float)height);

    untzReset();

    
    moonlander.start();
}

public void draw() {
    moonlander.update();

    // time in seconds
    float time = (float)moonlander.getCurrentTime() + song_offset_seconds;

    // calculate number of beats since start of demo
    float beats = (float)moonlander.getCurrentRow() / rows_per_beat;

    int scene = moonlander.getIntValue("scene");

    if (scene == 1) {
        drawCubeScene(time, beats);
    } else if (scene == 2) {
        drawCubeScene2(time, beats);
    } else if (scene == 3) {
        drawCubeMoveScene1(time, beats);
    } else if (scene == 4) {
        drawCubeSpinScene3(time, beats);
    } else if (scene == 5) {
        drawCubeSpinScene1(time, beats);
    } else if (scene == 6) {
        drawCubeSpinScene2(time, beats);
    } else if (scene == 7) {
        drawIntroScene(time, beats);
    } else if (scene == 8 ) {
        drawCubeSpinScene4(time, beats);
    } else if (scene == 9 ) {
        drawCubeMoveScene2(time, beats);
    } else if (scene == 10 ) {
        drawCubeSpinScene5(time, beats);
    } else if (scene == 11 ) {
        drawCubeScene4(time, beats);
    } else if (scene == 12 ) {
        drawShaderScene(time, beats);
    } else if (scene == 999) {
        drawBlackScene(time, beats);
    } else if (scene == 666) {
        exit();
    }

}
public void drawBlackScene(final float time, final float beats) {
    background(8);
    // just black, for "fadeout" after outro
}

public void drawCubeMoveScene1(final float time, final float beats) {
    background(16, 16, 16);

    noStroke();
    lights();
    //strokeWeight(1);
    //stroke(cubes_color_blue1);
    int boxes = 11;
    int boxSize = (int)(width/(boxes*1.25f));
    float off = (boxes+1.0f)/2;
    float space = boxSize + boxSize;//*(beats%1);
    pushMatrix();
    translate(width/2, height/2, 0);
    translate( space*(beats%1), space*(beats%1), 0);
    translate(-space*off, -space*off, -space*boxes);

    for (int i = 0; i < boxes; i++) {
      translate(0, space, 0);
      pushMatrix();
      for (int j = 0; j < boxes; j++) {
        translate(space, 0, 0);
        pushMatrix();
        for (int k = 0; k < boxes; k++) {
          translate(0, 0, space);
          int fill_color = lerpColor(
              cubes_color_blue1, cubes_color_blue2, (float)(i+j+k) / (boxes*3.0f));

          fill(fill_color);
          box(boxSize);
        }
        popMatrix();
      }
      popMatrix();
    }
    //box();
    popMatrix();
}

public void drawCubeMoveScene2(final float time, final float beats) {
    background(16, 16, 16);

    noStroke();
    lights();
    //strokeWeight(1);
    //stroke(cubes_color_blue1);
    int boxes = 9;
    int boxSize = (int)(width/(boxes*1.25f));
    float off = (boxes+1.0f)/2;
    float space = boxSize + boxSize/4.0f + 20.0f;//*(beats%1);
    float test = beats/8.0f * 3.14159265358979f;
    pushMatrix();
    translate(width/2, height/2, 0);
    float direction = (float)moonlander.getValue("direction");
    translate( cos(test)*space*0.6f, sin(test)*space*0.6f, space + direction*space*(beats%1) );
    translate(-space*off, -space*off, -space*off);

    for (int i = 0; i < boxes; i++) {
      translate(0, space, 0);
      pushMatrix();
      for (int j = 0; j < boxes; j++) {
        translate(space, 0, 0);
        pushMatrix();
        for (int k = 0; k < boxes; k++) {
          translate(0, 0, space);
          int fill_color = lerpColor(
              cubes_color_blue1, cubes_color_blue2, (float)(k*3.0f) / (boxes*3.0f));

          fill(fill_color);
          box(boxSize);
        }
        popMatrix();
      }
      popMatrix();
    }
    //box();
    popMatrix();
}


public void drawCubeSpinScene4(final float time, final float beats) {
    background(16, 16, 16);

    noStroke();
    lights();
    //strokeWeight(1);
    //stroke(cubes_color_blue1);

    int boxes = 10;//floor(beats)%10+1;
    int boxSize = (int)(width/(boxes*2.5f));
    float off = (boxes+1.0f)/2;
    float space = boxSize + boxSize/2;
    pushMatrix();
    translate(width/2, height/2, 0);
    rotateY(beats);
    translate(-space*off, -space*off, -space*off);

    for (int i = 0; i < boxes; i++) {
      translate(0, space, 0);
      rotateY(beats/4.0f);
      pushMatrix();
      for (int j = 0; j < boxes; j++) {
        translate(space, 0, 0);
        rotateX(beats/8.0f);
        pushMatrix();
        for (int k = 0; k < boxes; k++) {
          translate(0, 0, space);
          rotateZ(beats/16.0f);
          int fill_color = lerpColor(
              cubes_color_blue1, cubes_color_blue2, (float)(k*3.0f) / (boxes*3.0f));

          fill(fill_color);
          box(boxSize);
        }
        popMatrix();
      }
      popMatrix();
    }
    //box();
    popMatrix();
}

public void drawCubeSpinScene5(final float time, final float beats) {
    background(16, 16, 16);

    //strokeWeight(1);
    //stroke(cubes_color_blue1);
    noStroke();
    lights();

    int boxes = 10;//floor(beats)%10+1;
    int boxSize = (int)(width/(boxes*3.0f));
    float off = (boxes+1.0f)/2;
    float space = boxSize + boxSize/2.0f + boxSize/2.0f*(beats%1);
    pushMatrix();
    translate(width/2, height/2, 0);
    rotateY(beats);
    translate(-space*off, -space*off, -space*off);

    for (int i = 0; i < boxes; i++) {
      translate(0, space, 0);
      rotateY(beats/4.0f);
      pushMatrix();
      for (int j = 0; j < boxes; j++) {
        translate(space, 0, 0);
        rotateX(beats/8.0f);
        pushMatrix();
        for (int k = 0; k < boxes; k++) {
          translate(0, 0, space);
          rotateY(beats/16.0f);
          int fill_color = lerpColor(
              cubes_color_blue1, cubes_color_blue2, (float)(k*3.0f) / (boxes*3.0f));

          fill(fill_color);
          box(boxSize);
        }
        popMatrix();
      }
      popMatrix();
    }
    //box();
    popMatrix();
}

public void drawCubeSpinScene3(final float time, final float beats) {
    background(16, 16, 16);

    noStroke();
    lights();
    //strokeWeight(1);
    //stroke(cubes_color_blue1);

    int boxes = floor(beats)%10+1;
    int boxSize = (int)(width/(boxes*4));
    float off = (boxes+1.0f)/2;
    float space = boxSize + /*boxSize/8.0 +*/ boxSize/1.25f*(beats%1);
    pushMatrix();
    translate(width/2, height/2, 0);
    rotateY(beats*3.14159265358979f/4.0f);
    translate(-space*off, -space*off, -space*off);

    for (int i = 0; i < boxes; i++) {
      translate(0, space, 0);
      pushMatrix();
      for (int j = 0; j < boxes; j++) {
        translate(space, 0, 0);
        pushMatrix();
        for (int k = 0; k < boxes; k++) {
          translate(0, 0, space);
          int fill_color = cubes_color_blue2;//lerpColor(
              //cubes_color_blue1, cubes_color_blue2, (float)(k*3.0) / (boxes*3.0));

          fill(fill_color);
          box(boxSize);
        }
        popMatrix();
      }
      popMatrix();
    }
    //box();
    popMatrix();
}

public void drawCubeSpinScene1(final float time, final float beats) {
    background(16, 16, 16);

    noStroke();
    lights();
    int boxes = 3;
    int boxSize = 100;
    float space = 110;
    pushMatrix();
    translate(width/2, height/2, 0);
    //translate(-space*1.5,-space*1.5,-space*1.5);
    translate(-space*2, -space*2, -space*2);

    for (int i = 0; i < boxes; i++) {
      translate(0, space, 0);
      pushMatrix();
      for (int j = 0; j < boxes; j++) {
        translate(space, 0, 0);
        rotateY(beats);
        pushMatrix();
        for (int k = 0; k < boxes; k++) {
          translate(0, 0, space);
          int fill_color = cubes_color_blue2;

          fill(fill_color);
          box(boxSize);
        }
        popMatrix();
      }
      popMatrix();
    }
    //box();
    popMatrix();
}

public void drawCubeSpinScene2(final float time, final float beats) {
    background(16, 16, 16);

    noStroke();
    lights();
    int boxes = 3;
    int boxSize = 100;
    float space = 110;
    pushMatrix();
    translate(width/2, height/2, 0);
    //translate(-space*1.5,-space*1.5,-space*1.5);
    translate(-space*2, -space*2, -space*2);

    for (int i = 0; i < boxes; i++) {
      translate(0, space, 0);
      pushMatrix();
      for (int j = 0; j < boxes; j++) {
        translate(space, 0, 0);
        pushMatrix();
        for (int k = 0; k < boxes; k++) {
          translate(0, 0, space);
          rotateY(beats);
          int fill_color = cubes_color_blue2;

          fill(fill_color);
          box(boxSize);
        }
        popMatrix();
      }
      popMatrix();
    }
    //box();
    popMatrix();
}
final int cubes_color_blue1 = color(25, 52, 65);
final int cubes_color_blue2 = color(252, 255, 245);

public void drawCubeScene3(final float time, float beats) {
    // Simple version of the scene with single cube cluster slowly growing

    background(32);

    translate(width / 2, height / 2, 0);

    noStroke();

    pushMatrix();

    // looks nicer with some offset to the beat
    beats = beats + 0.3f;

    // scale the cubes to the beat
    float cube_bounce =
        0.0625f*(beats%(8*8));// * sin(6 * beats * PI);

    scale(0.5f + cube_bounce);

    cubes_DrawCubeCluster(time, beats);

    popMatrix();

}

public void drawCubeScene4(final float time, float beats) {
    // Simple version of the scene from inside cube cluster

    background(32);

    translate(width / 2, height / 2, 0);

    noStroke();

    pushMatrix();

    // looks nicer with some offset to the beat
    beats = beats + 0.3f;

    // scale the cubes to the beat
    float cube_bounce =
        abs(cos((beats % 1) * PI));// * sin(6 * beats * PI);

    scale(10 + cube_bounce);

    cubes_DrawCubeCluster(time, beats);

    popMatrix();

}

public void drawCubeScene(final float time, float beats) {
    // Simple version of the scene with single cube cluster

    background(32);

    translate(width / 2, height / 2, 0);

    noStroke();

    pushMatrix();

    // looks nicer with some offset to the beat
    beats = beats + 0.3f;

    // scale the cubes to the beat
    float cube_bounce =
        abs(cos((beats % 1) * PI));// * sin(6 * beats * PI);

    scale(1.0f + cube_bounce);

    cubes_DrawCubeCluster(time, beats);

    popMatrix();

}

public void drawCubeScene2(final float time, float beats) {
    // Alternate version of the scene with multiple cube clusters

    background(0);

    noStroke();

    // center to viewport
    translate(width / 2, height / 2, 0);

    // looks nicer with some offset to the beat
    beats = beats + 0.3f;

    float camera_away_movement = sin(time) * 10.0f;

    // Adjust camera
    translate(0.0f, 20.0f, 0.0f); // up a bit
    translate(0.0f, 0.0f, 30.0f + camera_away_movement); // away
    rotateX(-0.6f); // rotate downwards
    rotateY(time); // rotate around objects

    // floor
    pushMatrix();
        translate(0, 1.5f * height, 0);
        rectMode(CENTER);
        rotateX(0.5f * PI);
        //rect(0, 0, 6.0 * height, 6.0 * height);
        translate(-3.0f * height, -3.0f * height); // center the floor
        image(floor_texture, 0, 0, 6.0f * height, 6.0f * height);
    popMatrix();

    // three cubes

    for (int i = 0; i < 3; i++) {
        pushMatrix();

        rotateY(i * (TWO_PI / 3.0f));

        translate(0.0f, 0.0f,
            (float)moonlander.getValue("translateZ") * 100.0f);

        cubes_DrawCubeCluster(time, beats);

        popMatrix();
    }

}

public void cubes_DrawCubeCluster(final float time, final float beats) {
    for (int i = 0; i < 10; i++) {
        pushMatrix();

        lights();

        cubes_DrawCube(time, beats, i);

        popMatrix();
    }
}

public void cubes_DrawCube(float time, float beats, final int index) {
    int fill_color = lerpColor(
        cubes_color_blue1, cubes_color_blue2, index / 10.0f);

    fill(fill_color);

    time = time + 0.1f * index;

    rotateY(time);
    rotateX(time * 1.3f);
    rotateZ(time * 1.9f);

    box(height * 0.4f);
}

public void drawGradientScene(final float time, final float beats) {

    int from = color(100, 0, 153);
    int to = color(200, 20, 39);
    int lines = 9;
    drawGradient(from, to, lines);
}

//creates gradient from startcolor to endcolor using bands amount of colours
public void drawGradient(final int startcolor, final int endcolor, final int lines) {
  strokeWeight(0);
  rectMode(CORNER);
  for(int i = 0; i < lines; i++){
    float iterating = (float)i/((float)lines-1.0f);
    float iterating2 = (float)i/((float)lines);
    int inter = lerpColor(startcolor, endcolor, iterating);
    fill(inter);
    rect(0, height*iterating2, width, height/lines+1);//+1 is a hack
  }
}
public void drawIntroScene(final float time, final float beats) {
    background(8);

    int intro_text_select = moonlander.getIntValue("intro_text");

    // center the image
    translate((width / 2.0f) - (height / 2.0f), 0.0f, 0.0f);

    int untz_reset_signal = moonlander.getIntValue("untz_reset");

    if (untz_reset_signal == 1) {
        untzReset();
    }

    untz(0, 0, 0, 10, 100);
    translate(untz_x, untz_y);
    scale(1.0f + untz_x * 0.003f);

    if (intro_text_select == 1) {
        image(intro_header, 0, 0, height, height);
    } else if (intro_text_select == 2) {
        image(intro_qazhax, 0, 0, height, height);
    } else if (intro_text_select == 3) {
        image(intro_locketeerian, 0, 0, height, height);
    } else if (intro_text_select == 4) {
        image(intro_wno, 0, 0, height, height);
    } else if (intro_text_select == 5) {
        image(intro_createdby, 0, 0, height, height);
    } else if (intro_text_select == 6) {
        image(intro_music, 0, 0, height, height);
    }
}
public void drawShaderScene(final float time, final float beats) {
    background(0);

    shader(shader);
    shader.set("iGlobalTime", (float)moonlander.getCurrentTime());
    shader.set("iIntensity", (float)moonlander.getValue("translateZ"));
    //shader.set("texture", intro_header);
    beginShape();
    texture(fake_scene);
    vertex(0,0);
    vertex(width, 0);
    vertex(width, height);
    vertex(0, height);
    endShape();
    //rect(0,0,width, height);
}
// for use with the untz() and untzReset()
float untzCounter = 0;
float untz_xMax = random(-1, 1);
float untz_yMax = random(-1, 1);
float untz_zMax = random(-1, 1);
float untz_x = 0;
float untz_y = 0;
float untz_z = 0;

public void untzReset(){

  // randomize a orthogonal vector with components and normalise it

  untz_xMax = random(-1, 1);
  untz_yMax = random(-1, 1);
  untz_zMax = random(-1, 1);

  float scaleDown = sqrt(sq(untz_xMax) + sq(untz_yMax) + sq(untz_zMax));
  untz_xMax = untz_xMax / scaleDown;
  untz_yMax = untz_yMax / scaleDown;
  untz_zMax = untz_zMax / scaleDown;
  // advance the counter for the oscillation
  untzCounter = 0;
}

public void untz(float xx, float yy, float zz, float dampening, float maxAmplitude){

  untzCounter = untzCounter + 1;
  float temp = maxAmplitude * exp(-untzCounter / dampening)*sin(untzCounter);
  untz_x = xx + untz_xMax * temp;
  untz_y = yy + untz_yMax * temp;
  untz_z = zz + untz_zMax * temp;

}
  public void settings() {  fullScreen(P3D); }
  static public void main(String[] passedArgs) {
    String[] appletArgs = new String[] { "gthon2017" };
    if (passedArgs != null) {
      PApplet.main(concat(appletArgs, passedArgs));
    } else {
      PApplet.main(appletArgs);
    }
  }
}
