package giantglove;
import processing.core.*;
import visad.*;
import Jpeg.Jpeg;

//import processing.opengl.*;

//import processing.video.Movie;

public class Engine
    extends PApplet {

  public static void main(String[] args) {

    WIDTH = 640;
    HEIGHT = 480;
    PApplet.main(new String[] {Engine.class.getName()});

  }

  //---------------------------------------------
  static final boolean FULLSCREEN = false;
  static int WIDTH;
  static int HEIGHT;
  //
  boolean delaunay = false;
  float[] texx;
  float[] texy;
  float[] px;
  float[] py;
  PImage img;
  boolean showLines = true;
  int num = 0;
  int RES = 10; //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  int rw = 0;
  int rh = 0;
  String imgFile = "mj_bet_HD00001.jpg";
  int imgw;
  int imgh;
  float x0 = 0;
  float y0 = 0;
  boolean running = false;
  //
  DataFrame FM;
  DataFrame FMS[];
  int comma;
  String temp_f, temp_x, temp_y, temp_w, temp_h;
  int f, x, y, w, h;
  int fnum;
  float t, inc;
  int frameNum = 0;
  PFont font;
  float lastfx = 0;
  float lastfy = 0;
  boolean save = false;
  boolean time = true;
  //---------------------------------------------

  public void setup() {
    size(WIDTH, HEIGHT, P3D);
    readData("input.txt");
    getImage(imgFile);
    makeGrid();
    font = createFont("Arial", 17);
    textFont(font, 17);
    //load movie
  }

  public void draw() {
    background(0);
    if (img != null) {
      pushMatrix();
      translate(x0, y0);
      drawTriangles(triangles);
      rebounce(3);
      if (mousePressed) {
        suck(mouseX, mouseY, 70);
      }
      popMatrix();
    }
    else {
      println("------------- ERROR-----------------");
      println("   Image could not be loaded: " + imgFile);
      fill(0, 255, 0);
      rect(0, 0, width, height);
      fill(255, 0, 0);
      text("   Image could not be loaded: " + imgFile, 10, height / 2);
      //running = false;
    }
    if (running) {
      step();

    }
    drawGlove();
    render();
    if (time) {
      fill(255);
      text(fnum, 10, 20);
    }
  }

  //---------------------------------------------
  public void render() {
    if (save) {
      Jpeg.saveToJPG("michael" + nf(fnum, 5) + ".jpg", this);

    if (fnum == frameNum - 1) {
      save = false;
      stop();
    }
  }
  }

  public String insertFrame(String what, int frameNum) {
    /**
       Original Comment from Processing source:

       Check a string for #### signs to see if the frame number should be
       inserted. Used for functions like saveFrame() and beginRecord() to
       replace the # marks with the frame number. If only one # is used,
       it will be ignored, under the assumption that it's probably not
       intended to be the frame number.
     */

    int first = what.indexOf('#');
    int last = what.lastIndexOf('#');

    if ( (first != -1) && (last - first > 0)) {
      String prefix = what.substring(0, first);
      int count = last - first + 1;
      String suffix = what.substring(last + 1);
      return prefix + nf(frameNum, count) + suffix;
    }
    return what; // no change
  }

  public void bulb(float xi, float yi, int r) {
    xi -= x0;
    yi -= y0;
    r /= 2;
    if (xi > -2 * r && xi < imgw + 2 * r && yi > -2 * r && yi < imgh + 2 * r) {
      //ellipse(xi, yi,2*r,2*r);
      int xr = (int) (xi) / RES;
      int xmin = (int) max(0, xr - r / RES);
      int xmax = (int) min(rw, xr + r / RES);
      int yr = (int) (yi) / RES;
      int ymin = (int) max(0, yr - r / RES);
      int ymax = (int) min(rh, yr + r / RES);

      for (int a = xmin; a < xmax; a++) {
        for (int b = ymin; b < ymax; b++) {
          float d = dist(px[a + b * rw], py[a + b * rw], xi, yi);
          if (d < r) {
            px[a + b * rw] = texx[a + b * rw] +
                (r - d) * (px[a + b * rw] - xi) / (r);
            py[a + b * rw] = texy[a + b * rw] +
                (r - d) * (py[a + b * rw] - yi) / (r);
          }
        }
      }
    }
  }

  public void addPower(float xi, float yi, float vx, float vy, float r) {

    xi -= x0;
    yi -= y0;
    xi -= vx;
    yi -= vy;
    if (showLines) {
      stroke(0, 255, 0);
      ellipse(xi, yi, 7, 7);
      line(xi, yi, xi + vx, yi + vy);
    }
    if (xi > -2 * r && xi < imgw + 2 * r && yi > -2 * r && yi < imgh + 2 * r) {
      //ellipse(xi, yi,2*r,2*r);
      int xr = (int) (xi) / RES;
      int xmin = (int) max(0, xr - r / RES);
      int xmax = (int) min(rw, xr + r / RES);
      int yr = (int) (yi) / RES;
      int ymin = (int) max(0, yr - r / RES);
      int ymax = (int) min(rh, yr + r / RES);

      for (int a = xmin; a < xmax; a++) {
        for (int b = ymin; b < ymax; b++) {
          float d = dist(px[a + b * rw], py[a + b * rw], xi, yi);
          if (d < r) {
            px[a + b * rw] = texx[a + b * rw] +
                2 * vx * (r - d) / (r);
            py[a + b * rw] = texy[a + b * rw] +
                2 * vy * (r - d) / (r);
          }
        }
      }

    }

  }

  public void superBulb(float xi, float yi, int r) {
    xi -= x0;
    yi -= y0;
    r /= 2;
    if (xi > -2 * r && xi < imgw + 2 * r && yi > -2 * r && yi < imgh + 2 * r) {
      //ellipse(xi, yi,2*r,2*r);
      int xr = (int) (xi) / RES;
      int xmin = (int) max(0, xr - r / RES);
      int xmax = (int) min(rw, xr + r / RES);
      int yr = (int) (yi) / RES;
      int ymin = (int) max(0, yr - r / RES);
      int ymax = (int) min(rh, yr + r / RES);

      for (int a = xmin; a < xmax; a++) {
        for (int b = ymin; b < ymax; b++) {
          float d = dist(px[a + b * rw], py[a + b * rw], xi, yi);
          if (d < r) {

            px[a + b * rw] = texx[a + b * rw] +
                (r - d) * (r - d) * (px[a + b * rw] - xi) / (r * r);
            py[a + b * rw] = texy[a + b * rw] +
                (r - d) * (r - d) * (py[a + b * rw] - yi) / (r * r);
          }
        }
      }
    }
  }

  public void suck(float xi, float yi, int r) {
    xi -= x0;
    yi -= y0;
    r /= 2;
    //int r = 50;
    if (xi > -2 * r && xi < imgw + 2 * r && yi > -2 * r && yi < imgh + 2 * r) {
      //ellipse(xi, yi,2*r,2*r);
      int xr = (int) (xi) / RES;
      int xmin = (int) max(0, xr - r / RES);
      int xmax = (int) min(rw, xr + r / RES);
      int yr = (int) (yi) / RES;
      int ymin = (int) max(0, yr - r / RES);
      int ymax = (int) min(rh, yr + r / RES);

      for (int a = xmin; a < xmax; a++) {
        for (int b = ymin; b < ymax; b++) {
          float d = dist(px[a + b * rw], py[a + b * rw], xi, yi);
          if (d < r) {
            int i = a + b * rw;
            float tx = texx[a + b * rw] +
                1.2f * (r - d) * (xi - px[a + b * rw]) / (r);
            px[i] = px[i] + (tx - px[i]) / 1;
            float ty = texy[a + b * rw] +
                1.2f * (r - d) * (yi - py[a + b * rw]) / (r);
            py[i] = py[i] + (ty - py[i]) / 1;
          }
        }
      }
    }
  }

  public void readData(String textFile) {
    //load in data from text file
    //String lines[] = loadStrings("median_degap_smooth.txt");
    String temp_f, temp_x, temp_y, temp_w, temp_h;
    String lines[] = loadStrings(textFile);



    //make room for all objs
    FMS = new DataFrame[lines.length];
    frameNum = lines.length;
    //loop through text file, save data into frame objects
    for (int i = 0; i < lines.length; i++) {
      String percent = (int)(100 * i / lines.length)+"%";
      print (" "+percent+" ");
      if(i%20==0){
        println("");
      }
      //intit vars
      comma = 0;
      temp_f = "";
      temp_x = "";
      temp_y = "";
      temp_w = "";
      temp_h = "";

      //loop through all chars on the line
      for (int j = 0; j < lines[i].length(); j++) {
        char c = lines[i].charAt(j); //this is the char we are looking at
        if (c == ',') { //which comma are we on
          comma++;
        }
        if (comma == 0) { //break into multiple strings
          temp_f += c;
        }
        else if (comma == 1) {
          if (c != ',') {
            temp_x += c;
          }
        }
        else if (comma == 2) {
          if (c != ',') {
            temp_y += c;
          }
        }
        else if (comma == 3) {
          if (c != ',') {
            temp_w += c;
          }
        }
        else if (comma == 4) {
          if (c != ',') {
            temp_h += c;
          }
        }
      }

      //convert strings to ints
      f = Integer.valueOf(temp_f).intValue();
      x = Integer.valueOf(temp_x).intValue();
      y = Integer.valueOf(temp_y).intValue();
      w = Integer.valueOf(temp_w).intValue();
      h = Integer.valueOf(temp_h).intValue();
      t += inc;

      //load data into object
      FM = new DataFrame(f, x, y, w, h, t);
      FMS[i] = FM;
      //FMS[i].print();
    }
    println("there are " + lines.length + " lines in "+textFile);
  }

  public void rebounce(float speed) {
    for (int i = 0; i < num; i++) {
      px[i] = px[i] + (texx[i] - px[i]) / speed;
      py[i] = py[i] + (texy[i] - py[i]) / speed;
    }
  }

  public void getImage(String file) {
    img = loadImage(file);
    imgw = img.width;
    imgh = img.height;
    //resize(imgw, imgh);
    //x0 = (width - imgw) / 2;
    //y0 = (height - imgh) / 2;
  }

  public void makeGrid() {
    rw = 1 + (int) imgw / RES;
    rh = 1 + (int) imgh / RES;
    num = rw * rh;
    px = new float[num];
    py = new float[num];
    texx = new float[num];
    texy = new float[num];
    for (int a = 0; a < rw; a++) {
      for (int b = 0; b < rh; b++) {
        px[a + b * rw] = RES * a;
        py[a + b * rw] = RES * b;
        texx[a + b * rw] = RES * a;
        texy[a + b * rw] = RES * b;
      }
    }
    triangles = new int[0][0];
    makeDelaunay();

  }

  int[][] triangles; // de id's van de hoeken.
  int ntris; //

  public void showHideLines() {
    showLines = !showLines;
  }

  public void makeDelaunay() {

    try {
      float[][] matrix = getPointMatrix();
      int npoints = matrix[0].length;
      //DelaunayFast df = new DelaunayFast(matrix);
      DelaunayClarkson df = new DelaunayClarkson(matrix);
      // DelaunayWatson df = new DelaunayWatson(matrix);
      triangles = df.Tri;
      ntris = triangles.length;
    }
    catch (VisADException e) {
      System.out.println("caught error " + e);
    }
    delaunay = true;
  }

  public float[][] getPointMatrix() {
    float[][] matrix = new float[2][num];
    for (int i = 0; i < num; i++) {
      matrix[0][i] = px[i];
      matrix[1][i] = py[i];
    }
    return (matrix);
  }

  public void drawTriangles(int[][] tris) {
    if (delaunay) {
      int a, b, c;
      int maxI = tris.length;
      fill(255);
      strokeWeight(1);
      if (showLines) {
        stroke(255, 0, 0);
      }
      else {
        noStroke();
      }
      beginShape(TRIANGLES);
      texture(img);
      for (int i = 0; i < maxI; i++) {
        a = tris[i][0];
        b = tris[i][1];
        c = tris[i][2];
        //
        vertex(px[a], py[a], 0, texx[a], texy[a]);
        vertex(px[b], py[b], 0, texx[b], texy[b]);
        vertex(px[c], py[c], 0, texx[c], texy[c]);
      }
      endShape();
      //System.out.println("draw Triangles: #" + maxI);
    }
  }

//--------------------------------------------
  public void step() {
    fnum++;
    fnum %= frameNum;
    imgFile = insertFrame("mj_bet_HD#####.jpg", fnum);
    PGraphics g = this.createGraphics(width, height, JAVA2D);

    img = loadImage(imgFile);

  }

  public void drawGlove() {

    stroke(255, 255, 0);
    noFill();
    if (showLines) {
      //   ellipse(FMS[fnum].x, FMS[fnum].y, FMS[fnum].h + FMS[fnum].w, FMS[fnum].w + FMS[fnum].h);
    }
    if (lastfx == 0 && lastfy == 0) {
      lastfx = FMS[fnum].x;
      lastfy = FMS[fnum].y;
    }
    //  suck(FMS[fnum].x , FMS[fnum].y , FMS[fnum].h+FMS[fnum].w );
    // addPower(FMS[fnum].x, FMS[fnum].y,FMS[fnum].x-lastfx, FMS[fnum].y-lastfy,2*( FMS[fnum].h + FMS[fnum].w));
    // addPower(FMS[fnum].x, FMS[fnum].y,FMS[fnum].x-lastfx, FMS[fnum].y-lastfy, abs(FMS[fnum].y-lastfy) + abs(FMS[fnum].x-lastfx));
    bulb(FMS[fnum].x, FMS[fnum].y, FMS[fnum].h + FMS[fnum].w);
    //starCircle(FMS[fnum].x, FMS[fnum].y, FMS[fnum].h + FMS[fnum].w);
    lastfx = FMS[fnum].x;
    lastfy = FMS[fnum].y;
    // superBulb(FMS[fnum].x , FMS[fnum].y , FMS[fnum].h+FMS[fnum].w );

  }

//---------------------------------------------

  void thickLine(float x1, float y1, int c1, float x2, float y2, int c2) {
    float ox1 = x1;
    float oy1 = y1;
    float ox2 = x2;
    float oy2 = y2;

    float dX = ox2 - ox1 + 0.0001f;
    float dY = oy2 - oy1 + 0.0001f;
    float len = sqrt(dX * dX + dY * dY);

    float rh = g.strokeWeight / len;

    float dx0 = rh * dY;
    float dy0 = rh * dX;
    float dx1 = rh * dY;
    float dy1 = rh * dX;

    beginShape(QUADS);
    noStroke();

    fill(c1);
    vertex(ox1 + dx0, oy1 - dy0, 0);
    vertex(ox1 - dx0, oy1 + dy0, 0);

    fill(c2);
    vertex(ox2 - dx1, oy2 + dy1, 0);
    vertex(ox2 + dx1, oy2 - dy1, 0);

    endShape();

  }

  public void arrow(float x1, float y1, int c1, float x2, float y2, int c2) {
    float ox1 = x1;
    float oy1 = y1;
    float ox2 = x2;
    float oy2 = y2;

    float dX = ox2 - ox1 + 0.0001f;
    float dY = oy2 - oy1 + 0.0001f;
    float len = sqrt(dX * dX + dY * dY);

    float rh = g.strokeWeight / len;

    float dx0 = rh * dY;
    float dy0 = rh * dX;
    float dx1 = 0 * dY;
    float dy1 = 0 * dX;

    beginShape(QUADS);
    noStroke();

    fill(c1);
    vertex(ox1 + dx0, oy1 - dy0, 0);
    vertex(ox1 - dx0, oy1 + dy0, 0);
    fill(c2);
    vertex(ox2 - dx1, oy2 + dy1, 0);
    vertex(ox2 + dx1, oy2 - dy1, 0);
    endShape();

  }

  void starCircle(float xi, float yi, float r) {
    r /= 4;
    pushMatrix();
    translate(xi, yi);
    strokeWeight(r / 30 * PI);
    noStroke();
    for (int i = 0; i < 30; i++) {
      rotate( (360 / 30) * PI / 180);
      arrow(0f, -r, color(255, 0, 0), 0f, -2f * r, color(255, 255, 0, 0));
    }
    popMatrix();
  }

//-------------------------------------------------
  public void keyPressed() {
    if (key == CODED) {
      switch (keyCode) {

      }
    }
    else {
      switch (key) {
        case 'l':
          showHideLines();
          break;
        case ' ':
          running = !running;
          break;
          case'a':
            fnum = 0;
            println("back to start");
            break;
        case 's':
          running = !running;
          save = !save;
          if (save) {

            println("==================== starting to save! ==========");
          }
          else {
            println("================= end saving ==============");
          }
          break;
        case 't':
          time = !time;
          break;
      }
    }
  }

  public void mousePressed() {
  }

  public void mouseReleased() {

  }

  public void mouseDragged() {
    if (mouseButton == RIGHT) {
      fnum += mouseX - pmouseX;
      fnum += frameNum;
      fnum %= frameNum;
    }

  }

  //---------------------------------------------

  public void stop(){
    println(" program ended at " + millis());
    System.exit(0);
  }
}
