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





static boolean release = true;

Moonlander ml;
PGraphics frame, buffer;

int w = 1280;
int h = 720;

float whiteout;
float chroma_intens;
float bluramount;
int neon_intens;
float display_noise;

static int particlenum = 64;
static int rainnum = 32;

float[] dropx = new float[rainnum];
PVector[] dropdir = new PVector[rainnum];
float[] dropcol = new float[rainnum];
float[] droplen = new float[rainnum];

PShader post, neon, glitch;
PShape[] platonics = new PShape[5];

PVector[] particles = new PVector[particlenum];
PVector[] particle_v = new PVector[particlenum];

PFont f;

public void settings() {
  if (release) {
    fullScreen(P2D);
  }
  else {
    size(w, h, P2D);
  }
}

public void setup() {
  noCursor();
  frameRate(60);
  
  randomSeed(1337387);
  
  for (int i = 0; i < particlenum; i++) {
    float x = random(-640.0f, 640.0f);
    float y = random(-640.0f, 640.0f);
    float dx = random(-5.0f, 5.0f);
    float dy = random(-5.0f, 5.0f);
    particles[i] = new PVector(x, y);
    particle_v[i] = new PVector(dx, dy);
  }
  
  for (int i = 0; i < rainnum; i++) {
    float x = random(-720.0f, 720.0f);
    float dx = random(-10.0f, 10.0f);
    float dy = random(30.0f, 100.0f);
    float hue = random(0.0f, 360.0f);
    float len = random(50.0f, 100.0f);
    
    dropx[i] = x;
    dropdir[i] = new PVector(dx,dy);
    dropcol[i] = hue;
    droplen[i] = len;
  }
  
  ml = Moonlander.initWithSoundtrack(this, "The_Polish_Ambassador_-_09_-_Fax_Travel.mp3", 120, 4);
  ml.start();
  
  post = loadShader("Post.glsl");
  neon = loadShader("Neon.glsl");
  glitch = loadShader("Glitch.glsl");
  
  frame = createGraphics(width, height, P3D);
  buffer = createGraphics(w, h, P3D);
  
  f = loadFont("YanoneKaffeesatz-Regular-48.vlw");
  
  for (int i = 1; i <= 5; i++) {
    String name = "plato" + i + ".obj";
    PShape model = loadShape(name);
    platonics[i-1] = model;
  }
}

public void draw() {
  ml.update();
  frame.colorMode(HSB, 360.0f, 100.0f, 100.0f);
  
  int scene = (int)ml.getValue("scene");
  whiteout = (float)ml.getValue("whiteout");
  chroma_intens = (float)ml.getValue("chroma");
  bluramount = (float)ml.getValue("blurring");
  neon_intens = (int)ml.getValue("neon");
  display_noise = (float)ml.getValue("disp_noise");
  
  frame.beginDraw();
  frame.translate(width/2.0f, height/2.0f);
  frame.scale(height/720.0f);
  
  switch(scene) {
    case 1:
      pulssi();
      break;
    case 2:
      platon();
      break;
    case 3:
      particles();
      break;
    case 4:
      platon();
      break;
    case 5:
      neon_rain();
      break;
    case 6:
      particles();
      break;
    case 7:
      platon();
      break;
    case 8:
      growing_terrain();
      break;
    case 9:
      platon();
      break;
    case 10:
      neon_rain();
      break;
    case 11:
      platon();
      break;
    case 12:
      exit();
      break;
  }
  frame.endDraw();
  
  set_shader_params();
  
  PImage i = frame.get();
  
  buffer.beginDraw();
  buffer.image(i, 0.0f, 0.0f, w, h);
    
  if (chroma_intens > 0.0f || whiteout > 0.0f || bluramount > 0.0f) {
    buffer.shader(post);
    buffer.image(buffer.get(), 0, 0);
  }
  
  if (neon_intens > 0.0f) {
    buffer.shader(neon);
    buffer.image(buffer.get(), 0, 0);
  }
  
  if (display_noise > 0.0f) {
    buffer.shader(glitch);
    buffer.image(buffer.get(), 0, 0);
  }
  
  buffer.endDraw();
  
  image(buffer.get(), 0, 0, width, height);
}

public void set_shader_params() {
  post.set("whiteout", whiteout);  
  post.set("chroma", chroma_intens);
  post.set("blur", bluramount);
  
  if (display_noise > 0.0f) {
    float start = map(noise(display_noise), 0.0f, 1.0f, 0.0f, 0.99f);
    float str = map(noise(display_noise+200), 0.0f, 1.0f, -0.1f, 0.1f);
    float len = map(noise(display_noise+400), 0.0f, 1.0f, 0.01f, 0.1f);
    len = constrain(len, 0.0f, 1.0f-start);
    glitch.set("glitch_row", start);
    glitch.set("glitch_str", str);
    glitch.set("glitch_len", len);
  }
  else {
    post.set("glitch_row", -1.0f);
    post.set("glitch_str", 0.0f);
    post.set("glitch_len", 0.0f);
  }
  
  neon.set("strength", neon_intens);
}

public void pulssi() {
  float pulse = (float)ml.getValue("pulse") * 240;
  float pulse_rev = (float)ml.getValue("pulse_rev");
  frame.background(255);
  frame.noFill();
  frame.stroke(0, 0, 1);
  float weight = map((float)ml.getValue("opening"), 0.0f, 1.0f, 48.0f, 720);
  frame.strokeWeight(weight);
  frame.bezier(-680, 0, -120, -pulse * pulse_rev, 120, pulse * pulse_rev, 680, 0);
}

public void platon() {
  frame.background(0);
  float floor = (float)ml.getValue("floor");
  float scale = (float)ml.getValue("platon_scale");
  int platonic = (int)ml.getValue("platonic_idx");
  float rotx = (float)ml.getValue("platon_rotx");
  float roty = (float)ml.getValue("platon_roty");
  float rotz = (float)ml.getValue("platon_rotz");
  
  float scene_rot = (float)ml.getValue("scene_rot");
  float title_alpha = (float)ml.getValue("title_alpha");
  
  int credits = (int)ml.getValue("credits_idx");
  
  frame.textFont(f);
  frame.textSize(48.0f);
  frame.fill(0.0f, 0.0f, 100.0f, title_alpha);
  frame.text("Platonic Love", -96.0f, -100.0f, 360.0f);
  
  frame.textSize(8.0f);
  frame.fill(0.0f, 0.0f, 100.0f, 255.0f);
  switch(credits) {
    case 3:
      frame.text("Music:", -80.0f, 20.0f, 540.0f);
      frame.text("Fax Travel by The Polish Ambassador", -80, 30.0f, 540.0f);
    case 2:
      frame.text("Greets go to everyone at Graffathon 2018", -80.0f, 0.0f, 540.0f);
    case 1:
      frame.text("A production by ruuben", -80.0f, -20.0f, 540.0f);
      break;
  }
  
  float zoom = (float)ml.getValue("platon_zoom");
  frame.translate(360 * zoom, 0.0f);
  frame.scale(zoom + 1.0f);
  
  frame.stroke(10.0f, 60, 80, floor);
  frame.strokeWeight(5.0f);
  
  frame.translate(0.0f, 0.0f, 450);
  frame.rotateY(scene_rot);
  frame.translate(0.0f, 0.0f, -450);
  
  frame.translate(0.0f, 40.0f, 400.0f);
  for (int i = 0; i <= 10; i++) {
    frame.line(-50 + 10*i, 0.0f, 0.0f,        -50 + 10*i, 0.0f, 100.0f);
    frame.line(-50.0f,      0.0f, 0.0f + 10*i, 50.0f,       0.0f, 0 + 10*i);
  }
  
  if (platonic >= 1 && platonic <= 5) {
    frame.pushMatrix();
    
    frame.translate(0.0f, -50.0f, 50.0f);
    frame.resetShader();
    frame.lights();
    frame.pointLight(0.0f, 0.0f, 100.0f, -50.0f, -50.0f, 50.0f);
    
    frame.scale(scale);
    
    frame.rotateY(roty);
    frame.rotateZ(rotz);
    frame.rotateX(rotx);
    
    frame.shape(platonics[platonic-1]);
    frame.noLights();
    frame.popMatrix();
  }  
}

public void particles() {
  frame.background(255.0f);
  frame.strokeWeight(3.0f);
  frame.stroke(0.0f, 0.0f, 25.0f, 255.0f);
  frame.fill(0.0f, 0.0f, 10.0f, 255.0f);
  
  float t = (float)ml.getValue("particle_t");
  float noise = (float)ml.getValue("particle_noise");
  
  PVector[] displacement = new PVector[particlenum];
  for (int i = 0; i < particlenum; i++) {
    if (noise > 0.0f) {
      float x = noise((float)i, 0.0f, t) * noise;
      float y = noise((float)i, 10.0f, t) * noise;
      displacement[i] = new PVector(x,y);
    }
    else {
      displacement[i] = new PVector(0.0f, 0.0f);
    }
  }
  
  for (int i = 0; i < particlenum; i++) {
    frame.ellipse(particles[i].x + t*particle_v[i].x + displacement[i].x, particles[i].y + t*particle_v[i].y + displacement[i].y, 10, 10);
  }
  
  frame.strokeWeight(2.0f);
  for (int i = 0; i < particlenum; i++) {
    for (int j = i; j < particlenum; j++) {
      PVector pos1 = new PVector(particles[i].x + t*particle_v[i].x + displacement[i].x, particles[i].y + t*particle_v[i].y + displacement[i].y);
      PVector pos2 = new PVector(particles[j].x + t*particle_v[j].x + displacement[j].x, particles[j].y + t*particle_v[j].y + displacement[j].y);
      float distx = pos1.x - pos2.x;
      float disty = pos1.y - pos2.y;
      float dist = sqrt(distx*distx + disty*disty);
      float a = map(255.0f / dist, 1.0f, 3.0f, 0.0f, 255.0f);
      frame.stroke(0.0f, 0.0f, 25.0f, a);
      frame.line(pos1.x, pos1.y, pos2.x, pos2.y);
    }
  }
}

public void neon_rain() {
  frame.background(0.0f);
  frame.strokeWeight(1.5f);
  
  float t = (float)ml.getValue("drop_t");
  
  for (int i = 0; i < rainnum; i++) {
    frame.stroke(dropcol[i], 60.0f, 100.0f, 255.0f);
    float x1 = dropx[i] + t*dropdir[i].x;
    float y1 = -360 + t*dropdir[i].y - i*dropdir[i].y;
    
    float x2 = x1 - droplen[i]*dropdir[i].x;
    float y2 = y1 - droplen[i]*dropdir[i].y;
    
    frame.line(x1, y1, x2, y2);
  }
}

public void growing_terrain() {
  float t = (float)ml.getValue("terrain_t");
  float ampl = (float)ml.getValue("terrain_ampl");
  
  frame.background(0.0f);
  frame.stroke(10.0f, 60, 80, 16.0f);
  frame.strokeWeight(5.0f);
  frame.noFill();
  
  frame.pushMatrix();
  frame.translate(0.0f, 100.0f, 0.0f);
  frame.rotateY(PI/8);
  frame.rotateX(-PI/8);
  frame.translate(0.0f, 0.0f, 0.0f);
  
  float h;
  for (int i = 0; i < 20; i++) {
    frame.beginShape(QUAD_STRIP);
    for (int j = 0; j <= 20; j++) {
      h = -1.0f * noise(i, j, t) * ampl;
      frame.vertex(-400.0f + 40*i, h, -400.0f + 40*j);
      h = -1.0f * noise(i+1.0f, j, t) * ampl;
      frame.vertex(-400.0f + 40*i + 40.0f, h, -400.0f+40*j);
    }
    frame.endShape();
  }
  frame.popMatrix();
}
  static public void main(String[] passedArgs) {
    String[] appletArgs = new String[] { "Graffathon" };
    if (passedArgs != null) {
      PApplet.main(concat(appletArgs, passedArgs));
    } else {
      PApplet.main(appletArgs);
    }
  }
}
