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

import java.util.*; 
import java.util.Random; 
import moonlander.library.*; 
import ddf.minim.*; 
import processing.opengl.*; 

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









ArrayList<Shape> shapes = new ArrayList<Shape>();
Random random = new Random();
Moonlander moonlander;

int balls = 0;
int spawnInterval = 0;
PShape obu;

public void setup()
{
  moonlander = Moonlander.initWithSoundtrack(this, "nils.mp3", 127, 1);
  size(600, 600, P3D);
  for ( int i = 0; i < 60; i++) {
    Shape shape = new Shape((int)random(width-10), (int)random(height-10), (random.nextBoolean() ? -1 : 1), (random.nextBoolean() ? -1 : 1));
    shape.active = true;
    shapes.add(shape);
  }

  obu = loadShape("PiggyBank.obj");
  moonlander.start();
}

public void draw() {

  int opacity = (int)moonlander.getValue("background_opacity");
  background(opacity);
  int time = millis();

  moonlander.update();
  int spawn = (int)moonlander.getValue("spawn_shape");
  int mode = (int)moonlander.getValue("mode");
  float pigLight = (float) moonlander.getValue("pig_light");

  if (mode <=1) {
    if ( mode == 1 ) {
      mergeMode(mode);
    }

    for (int i = 0; i < spawn; i++) {
      shapes.get(i).Update(time);
      shapes.get(i).Draw(); 
      curFrameCount = frameCount;
    }
  } else if (mode == 2) {

    Spiral();
    curFrameCount2 = frameCount;
  } else if (mode == 3) {
    colorMode(HSB, 1);
    multiLines(curFrameCount2);
  } else if (mode == 4) {
    colorMode(RGB, 255, 255, 255);
    piggy(opacity, pigLight  );
    exitTime = millis();
  } else {
    piggy(opacity, pigLight  );
    if (millis() - exitTime >10000)
    exit();
  }
}
int exitTime;
float theta1;
float theta2;
public void piggy(int opacity, float pigLight){

    background(opacity);
    
    translate(300, 366, 10);
    theta1+=0.05f;

    directionalLight(230, 230, 0, 0, 0, pigLight);
    ambientLight(102, 102, 102);
    
    scale(50);
    rotateY(4.5f);
    rotateY(millis()*0.0005f);
    rotateZ(PI);
    shape(obu);
  
}
int curFrameCount2;
int curTime = 0;
int lastTime = 0;
int numLine = 0;
int numDot = 1000;
float bSize = 100.0f;


public void multiLines(int curFrameCount) {
  int fc = frameCount - curFrameCount;
  background(0);  
  curTime = millis();
 

  if (curTime - lastTime >1000)
  {
    if (numLine <10) {
      numLine = numLine + 1;
      lastTime = curTime;
    }
  }

  for ( int j=0; j< numLine; j++) {
    float hue_incr = fc * .001f + .1f;
    float hue = hue_incr*j;
    hue -= Math.floor(hue);
    fill( hue-noise(j/1.5f), 100-noise(j/0.1f), 100*noise(j/0.5f));

    beginShape();
    stroke(155,0,255);
    float f1 = noise(j/10.0f)+0.5f;
    float f2 = noise(j/7.0f);  
    float f3 = noise(j/1.3f)+.5f;
    float f4 = noise(j/1.2f, fc/500.0f);
    float f5 = noise(j/5.0f);
    float f6 = noise(j/1.8f)+.9f;
    int step = (int)bSize ;
    for ( int i=0; i<numDot; i+=PApplet.parseInt (step)) {
      ellipse(i, height/2+(800*f4)*sin(f1*TWO_PI*i/600 + 100*f2 - fc/(100.0f*f3) + 150*f5 * 100.0f*f6), bSize, bSize);
    }
    endShape();
  }
  if (numLine == 10.0f) {
    if (bSize > 1.5f) bSize -=0.2f;
  } 
  for ( int j=0; j< numLine; j++) {
    float hue_incr = fc * .001f + .1f;
    float hue = hue_incr*j;
    hue -= Math.floor(hue);
    fill( 0, 0, 0);

    beginShape();
    noStroke();
    float f1 = noise(j/10.0f)+0.5f;
    float f2 = noise(j/7.0f);  
    float f3 = noise(j/1.3f)+.5f;
    float f4 = noise(j/1.2f, fc /500.0f);
    float f5 = noise(j/5.0f);
    float f6 = noise(j/1.8f)+.9f;
    int step = (int)bSize ;
    for ( int i=0; i<numDot; i+=PApplet.parseInt (step)) {
      ellipse(i, height/2+(800*f4)*sin(f1*TWO_PI*i/600 + 100*f2 - fc/(100.0f*f3) + 150*f5 * 100.0f*f6), bSize, bSize);
    }
    endShape();
  }
}


int curFrameCount;
int   nbr_circles = 75;
float angle_incr = 2*PI / nbr_circles;
int dim = 0;
public void drawSpiral(int col, int curFrameCount) {
  fill(col);
  float elapsedSeconds = millis()*0.001f;
  float angle_incr = radians(2 + (frameCount-curFrameCount)/16.0f);

  float cx = width/2;
  float cy = height/2;
  float outer_rad = width*.45f;

  float sm_diameter = dim;

  for (int i = 1; i <= nbr_circles; ++i) {
    float ratio = i/(float)nbr_circles;
    float spiral_rad = ratio * outer_rad;
    float angle = i*angle_incr;
    float x = cx + cos(angle) * spiral_rad;
    float y = cy + sin(angle) * spiral_rad;

    // draw tiny circle at x,y
    ellipse(x, y, sm_diameter, sm_diameter);
  }
}
public void Spiral() {
  dim +=4;
  stroke(2);
  strokeWeight(1);
  drawSpiral(255, curFrameCount);
}

static class CONFIG {  
  public static boolean MERGE = false;
  public static int VELOCITY = 2;
}

public void mergeMode(int value) {
  CONFIG.MERGE = true;
  CONFIG.VELOCITY = value*2;
}

class PointXY {
  float x, y;
  int c;
  PointXY(float pX, float pY, int curColor) {
    x = pX;
    y = pY;
    c = curColor;
  }
}

class Shape
{
  float x;
  float y;
  float velocity;

  float xSign;
  float ySign;

  int size;
  int maxSize;

  boolean active;
  int curColor;

  LinkedList<PointXY> trailPoints;

  Shape(int startX, int startY, int signX, int signY)
  {
    x = (float)startX;
    y = (float)startY;

    xSign = signX;
    ySign = signY;


    this.colorPicker();

    maxSize = (int)random(10, 20);
    size = 0;

    active = true;
    trailPoints = new LinkedList<PointXY>();
  }  

  public void Draw() {   
    for (int i = trailPoints.size ()-1; i >= 0; --i) {

      noStroke();
      fill(trailPoints.get(i).c, 150-i*5);
      ellipse(trailPoints.get(i).x, trailPoints.get(i).y, size, size);
    }
  }

  public void Update(int time) {

    CollisionCheck();

    if ( size < maxSize ) {
      size += 10;
    } else if ( size > maxSize ) {
      size -= 10;
    }

    x += xSign * noise(x*frameCount)*1.8f * CONFIG.VELOCITY;
    y += ySign * noise(y*frameCount)*1.8f * CONFIG.VELOCITY;

    if ( !CONFIG.MERGE && trailPoints.size() > 50) {
      trailPoints.removeLast();
    } else if ( CONFIG.MERGE )
    {
      trailPoints.clear();
    }

    trailPoints.addFirst(new PointXY(x, y, curColor));
  }

  public void CollisionCheck() {

    for (int i=0; i < shapes.size (); i++) {
      if (shapes.get(i) != this && shapes.get(i).active) {
        Shape cShape = shapes.get(i);
        float cDist = dist(x, y, cShape.x, cShape.y);
        float colDist = cDist - ((size+cShape.size) / 2);

        if ( colDist < 0) {

          if ( !CONFIG.MERGE ) { 

            // Bounce shape logic
            cShape.xSign = cShape.xSign * -1;
            cShape.ySign = cShape.ySign * -1;
            xSign = xSign * -1;
            ySign = ySign * -1;
          } else {

            // Shape merge logic
            if ( this.size >= cShape.size ) {
              this.maxSize += cShape.size;
              cShape.maxSize = 0;
            } else {
              cShape.maxSize += this.size;
              this.maxSize = 0;
            }
          }

          // Color switch logic
          if ( cShape.curColor == this.curColor ) {
          } else {
            cShape.curColor = this.curColor;
          }
        }
      }
    }

    if ( y - size/2 <= 0 ) {
      ySign = 1;
      this.colorPicker();
    } else if (y + size/2 >= height) {
      ySign = -1;
      this.colorPicker();
    }

    if ( x - size/2 <= 0 ) {
      xSign = 1;
      this.colorPicker();
    } else if ( x + size/2 >= width ) {
      xSign = -1;
      this.colorPicker();
    }
  }

  private void colorPicker() {
    int colorPicker = (int)random(1, 4);

    switch(colorPicker) {
      case(1):
      curColor = color(random(100, 255), 0, 0);
      break;
      case(2):
      curColor = color(0, random(100, 255), 0);
      break;  
      case(3):
      curColor = color(0, 0, random(100, 255));
      break;
    };
  }
}

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