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

import ddf.minim.*; 

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

public class timeliner_pde extends PApplet {



AudioPlayer player;
Minim minim;//audio context

PImage sprite;
PFont font;
PShader toon;

int npartTotal = 10000;
int npartPerFrame = 10;
float speed = 2.0f;
float gravity = 0.05f;

int partLifetime;
PVector positions[];
PVector velocities[];
PVector varpos[];
int lifetimes[];  

int fcount, lastm;
float frate;
int fint = 3;

PGraphics buf;

String text[];

public void stop()
{
  player.close();
  minim.stop();
  super.stop();
}

public boolean sketchFullScreen() {
  return true;
}

public void setup() {
  size(640, 480, P3D);
  buf = createGraphics(width, height, P3D);
  frameRate(30);
  
  minim = new Minim(this);
  player = minim.loadFile("music.mp3", 2048);

  font = loadFont("SoulLotion-Italic-32.vlw");
  textFont(font,32);

  text = loadStrings("text.txt");

  background(255);
  sprite = loadImage("sprite.png");

  partLifetime = npartTotal / npartPerFrame;
  initPositions();
  initVelocities();
  initLifetimes(); 
  
  toon = loadShader("edges.glsl");

  // Writing to the depth buffer is disabled to avoid rendering
  // artifacts due to the fact that the particles are semi-transparent
  // but not z-sorted.
  hint(DISABLE_DEPTH_MASK);
  
  // Testing some hints
  //hint(DISABLE_TRANSFORM_CACHE);
  //hint(ENABLE_ACCURATE_2D);

  player.play();
  player.pause();
} 

float timer;
float starttime;
boolean firsttime = true;

boolean nustart = false;
float starter = 0;
public void draw()
{
  if (nustart == false) {
    starter = millis();
  player.play();
  }
  nustart = true;
  timer = millis();
//  println(timer);

  filter(toon);

  if (!player.isPlaying()) exit();

  if (timer < 110000)
    draw_colordream();
  else
    {
      if (firsttime) starttime = timer;
      firsttime = false;
    draw_princess();
    }


    // text
    
    float xpos = 20+cos(millis()*0.001f)*width*0.01f;
    float ypos = 0+abs(sin(millis()*0.000009f)*height);
    int ti = (int)(millis()*0.0001f);
    if (ti > 15) ti = 15;
    fill(255-millis()*0.2f,255-millis()*0.2f,255-millis()*0.2f,3+abs(cos(millis()*0.001f)*(16+millis()*0.001f)));
    text(text[ti],xpos,ypos);
    fill(millis()*0.002f,4+abs(cos(millis()*0.001f)*(16+millis()*0.001f)));
    text(text[ti],xpos+2,ypos+2);
}

public void draw_colordream() {
  for (int n = 0; n < npartTotal; n++) {
    lifetimes[n]++;
    if (lifetimes[n] == partLifetime) {
      lifetimes[n] = 0;
    }      

    if (0 <= lifetimes[n]) {      
      float opacity = 1.0f - PApplet.parseFloat(lifetimes[n]) / partLifetime;
            
      if (lifetimes[n] == 0) {
        // Re-spawn dead particle
        positions[n].x = width/2;
        positions[n].y = height/2;
        
        float angle = millis()*0.01f;
        float s = random(0.5f * speed, 0.5f * speed);
        velocities[n].x = s * cos(angle);
        velocities[n].y = s * sin(angle);
      } else {
        positions[n].x += velocities[n].x;
        positions[n].y += velocities[n].y;
        
        //velocities[n].y += gravity;
      }
      
      varpos[n].x = positions[n].x + tan(millis()*0.0000001f*positions[n].x)*width/2;
      varpos[n].y = positions[n].y + tan(millis()*0.0000001f*positions[n].y)*height/2;
      drawParticle2(varpos[n], opacity, lifetimes[n]);
    }
  }
  
  fcount += 1;
  int m = millis();
  if (m - lastm > 1000 * fint) {
    frate = PApplet.parseFloat(fcount) / fint;
    fcount = 0;
    lastm = m;
  } 
}

public void draw_princess () {

  for (int n = 0; n < npartTotal; n++) {
    lifetimes[n]++;
    if (lifetimes[n] == partLifetime) {
      lifetimes[n] = 0;
    }      

    if (0 <= lifetimes[n]) {      
      float opacity = 1.0f - PApplet.parseFloat(lifetimes[n]) / partLifetime;
            
      if (lifetimes[n] == 0) {
        // Re-spawn dead particle
        positions[n].x = width/2;
        positions[n].y = height/2;
        
        float angle = (millis()-starttime)*0.01f;
        float s = random(0.5f * speed, 0.5f * speed);
        velocities[n].x = s * cos(angle);
        velocities[n].y = s * sin(angle);
      } else {
        positions[n].x += velocities[n].x;
        positions[n].y += velocities[n].y;
        
        //velocities[n].y += gravity;
      }
      
      varpos[n].x = positions[n].x + cos((millis()-starttime)*0.001f)*width/16;
      varpos[n].y = positions[n].y + sin((millis()-starttime)*0.001f)*height/16;

      drawParticle2(varpos[n], opacity, lifetimes[n]);
    }
  }
  
  fcount += 1;
  int m = millis();
  if (m - lastm > 1000 * fint) {
    frate = PApplet.parseFloat(fcount) / fint;
    fcount = 0;
    lastm = m;
  } 
}

public void drawParticle1(PVector center, float opacity, float lifetime) {
  
  beginShape(QUAD);
  noStroke();
  
  float partSize = 40-40*lifetime / partLifetime;
  
  float r = abs(cos(millis()*0.00001f*partSize)*255)-partSize*2;
  float g = 128+tan(millis()*0.00001f*partSize)*64-partSize*1;
  float b = 92+atan(millis()*0.00001f*partSize)*64-partSize*2;
  tint(r, g, b, opacity * 255-partSize*4);
  texture(sprite);
  normal(0, 0, 1);
  vertex(center.x - partSize/2, center.y - partSize/2, 0, 0);
  vertex(center.x + partSize/2, center.y - partSize/2, sprite.width, 0);
  vertex(center.x + partSize/2, center.y + partSize/2, sprite.width, sprite.height);
  vertex(center.x - partSize/2, center.y + partSize/2, 0, sprite.height);                
  endShape();  
}

public void drawParticle2(PVector center, float opacity, float lifetime) {
  beginShape(QUAD);
  noStroke();
  
  float partSize = cos((millis()-starttime)*0.001f+center.x+center.y)*40-40*lifetime / partLifetime;
  
  float r = abs(cos((millis()-starttime)*0.00001f*partSize)*255)-partSize*3;
  float g = 128+cos((millis()-starttime)*0.00001f*partSize)*64-partSize*3;
  float b = 92+sin((millis()-starttime)*0.00001f*partSize)*64-partSize*3;
  tint(r, g, b, 12+1*partSize*cos(lifetime*(millis()-starttime)*0.000002f));
  texture(sprite);
  normal(0, 0, 1);
  vertex(center.x - partSize/2, center.y - partSize/2, 0, 0);
  vertex(center.x + partSize/2, center.y - partSize/2, sprite.width, 0);
  vertex(center.x + partSize/2, center.y + partSize/2, sprite.width, sprite.height);
  vertex(center.x - partSize/2, center.y + partSize/2, 0, sprite.height);                
  endShape();  
}

public void initPositions() {
  positions = new PVector[npartTotal];
  varpos = new PVector[npartTotal];
  for (int n = 0; n < positions.length; n++) {
    positions[n] = new PVector();
    positions[n].x = width/2;
    positions[n].y = height/2;
    varpos[n] = new PVector();
  }  
}

public void initVelocities() {
  velocities = new PVector[npartTotal];
  for (int n = 0; n < velocities.length; n++) {
    velocities[n] = new PVector();
  }
}

public void initLifetimes() {
  // Initializing particles with negative lifetimes so they are added
  // progressively into the screen during the first frames of the sketch   
  lifetimes = new int[npartTotal];
  int t = -1;
  for (int n = 0; n < lifetimes.length; n++) {    
    if (n % npartPerFrame == 0) {
      t++;
    }
    lifetimes[n] = -t;
  }
} 
  static public void main(String[] passedArgs) {
    String[] appletArgs = new String[] { "--full-screen", "--bgcolor=#666666", "--hide-stop", "timeliner_pde" };
    if (passedArgs != null) {
      PApplet.main(concat(appletArgs, passedArgs));
    } else {
      PApplet.main(appletArgs);
    }
  }
}
