import processing.core.*; 
import processing.xml.*; 

import com.processinghacks.triangulate.*; 

import java.applet.*; 
import java.awt.*; 
import java.awt.image.*; 
import java.awt.event.*; 
import java.io.*; 
import java.net.*; 
import java.text.*; 
import java.util.*; 
import java.util.zip.*; 
import java.util.regex.*; 

public class game_map2 extends PApplet {




Vector triangles = new Vector();
Vector points = new Vector();

int nodeCount;
Node[] nodes = new Node[100];
HashMap nodeTable = new HashMap(  );

int edgeCount;
Edge[] edges = new Edge[500];

static final int nodeColor = 0xffF0C070;
static final int selectColor = 0xffFF3030;
static final int fixedColor = 0xffFF8080;
static final int edgeColor = 0xff000000;

PFont font;
boolean paused;
boolean showEdges;
boolean triangulate;
int thresh;

public void setup(  ) {
  //size(1920, 1200);
  size(600, 600);
  loadEdgelist("script N res 7.0 edgelist.txt" );
  font = createFont("SansSerif", 10);
  textFont(font);
  smooth(  );
  paused=false;
  showEdges=false;
  triangulate=false;
  thresh=50;
}

public void loadEdgelist(String filename) {
  String lines[];
  lines = loadStrings(filename);
  for (int i = 0; i < lines.length; i++) {
    String[] pieces = splitTokens(lines[i], " :{}'"); 
    // Load data into array
    if((pieces[0].equals("#")==false)&&(pieces.length==4)) {
      float weight=PApplet.parseFloat(pieces[3]);
      addEdge(pieces[0], pieces[1], weight);
    }

  }

}

public void loadData(  ) {
  String[] lines = loadStrings("script.txt");
  // Make the text into a single String object.
  String line = join(lines, " ");
  // Replace -- with an actual em dash.
  line = line.replaceAll("--", "\u2014");

  // Split into phrases using any of the provided tokens.
  String[] phrases = splitTokens(line, ".,;:?!\u2014\"");
  for (int i = 0; i < phrases.length; i++) {
    // Make this phrase lowercase.
    String phrase = phrases[i].toLowerCase(  );
    // Split each phrase into individual words at one or more spaces.
    String[] words = splitTokens(phrase, " ");
    for (int w = 0; w < words.length-1; w++) {
      addEdge(words[w], words[w+1]);
    }
  }
}

public void addEdge(String fromLabel, String toLabel) {
  addEdge(fromLabel, toLabel, 50);
}

public void addEdge(String fromLabel, String toLabel, float weight) {
  // Filter out unnecessary words.
  if (ignoreWord(fromLabel) || ignoreWord(toLabel)) return;

  Node from = findNode(fromLabel);
  Node to = findNode(toLabel);
  from.increment(  );
  to.increment(  );

  // Check to see whether this Edge already exists.
  for (int i = 0; i < edgeCount; i++) {
    if (edges[i].from == from && edges[i].to == to) {
      edges[i].increment(  );
      return;
    }
  }
  Edge e = new Edge(from, to, weight);
  e.increment(  );
  if (edgeCount == edges.length) {
    edges = (Edge[]) expand(edges);
  }
  edges[edgeCount++] = e;
}

String[] ignore = { 
  "a", "of", "the", "i", "it", "you", "and", "to" };

public boolean ignoreWord(String what) {
  for (int i = 0; i < ignore.length; i++) {
    if (what.equals(ignore[i])) {
      return true;
    }
  }
  return false;
}

public Node findNode(String label) {
  label = label.toLowerCase(  );
  Node n = (Node) nodeTable.get(label);
  if (n == null) {
    return addNode(label);
  }
  return n;
}

public Node addNode(String label) {
  Node n = new Node(label);
  if (nodeCount == nodes.length) {
    nodes = (Node[]) expand(nodes);
  }
  nodeTable.put(label, n);
  nodes[nodeCount++] = n;
  return n;
}

public void draw(  ) {
  background(255);
  //background(32, 32, 192);

  if(!paused) {
    for (int i = 0 ; i < edgeCount ; i++) {
      if(edges[i].weight()>(thresh/100.0f)) edges[i].relax(  );
    }
    for (int i = 0; i < nodeCount; i++) {
      nodes[i].relax(  );
    }
    for (int i = 0; i < nodeCount; i++) {
      nodes[i].update(  );
      if(triangulate) points.addElement(new Point3f(nodes[i].x, nodes[i].y, 0));
    }

  };

  if(triangulate)triangles = Triangulate.triangulate(points);

  if(showEdges) {
    for (int i = 0 ; i < edgeCount ; i++) {
      if(edges[i].weight()>(thresh/100.0f)) edges[i].draw(  );
    }
  }

  for (int i = 0 ; i < nodeCount ; i++) {
    nodes[i].draw(  );
  }

  if(triangulate) {
    stroke(0);
    //fill(255, 0, 0,40);
    noFill();
    beginShape(TRIANGLES);
  
    for (int i = 0; i < triangles.size(); i++) {
      Triangle t = (Triangle)triangles.elementAt(i);
      vertex(t.p1.x,t.p1.y);
      vertex(t.p2.x,t.p2.y);
      vertex(t.p3.x,t.p3.y);
    }
    endShape();
  }
  
  fill(128,0,0);
  text(str(thresh), 30, 10);  
}

Node selection;

public void mousePressed(  ) {
  // Ignore anything greater than this distance.
  float closest = 20;
  for (int i = 0; i < nodeCount; i++) {
    Node n = nodes[i];
    float d = dist(mouseX, mouseY, n.x, n.y);
    if (d < closest) {
      selection = n;
      closest = d;
    }
  }
  if (selection != null) {
    if (mouseButton == LEFT) {
      selection.fixed = true;
    } 
    else if (mouseButton == RIGHT) {
      selection.fixed = false;
    }
  }
}

public void keyPressed() {
  if(key==' ') paused=(paused==false);
  if(key=='s') saveFrame("relatedness-####.tif");
  if(key=='+')
    thresh+=5;
  if(key=='-')
    thresh-=5;
  if(key=='e') showEdges=(showEdges==false);
  if(key=='t') triangulate=(triangulate==false);
}

public void mouseDragged(  ) {
  if (selection != null) {
    selection.x = mouseX;
    selection.y = mouseY;
  }
}

public void mouseReleased(  ) {
  selection = null;
}










class Edge {
  Node from;
  Node to;
  float len;
  float weight;
  
  Edge(Node from, Node to) {
    this.from = from;
    this.to = to;
    this.len = 50;
  }

  Edge(Node from, Node to, float weight) {
    this.from = from;
    this.to = to;
    this.len = 50.0f*(1.0f-weight);
    println(len);
    this.weight = weight;
}

  int count;

  public void increment(  ) {
    count++;
  }

  public float weight() {
    return weight;
  }
  
  public void relax(  ) {
    float vx = to.x - from.x;
    float vy = to.y - from.y;
    float d = mag(vx, vy);
    if (d > 0) {
      float f = (len - d) / (d * 3);
      float dx = f * vx;
      float dy = f * vy;
      to.dx += dx;
      to.dy += dy;
      from.dx -= dx;
      from.dy -= dy;
    }
  }

  public void draw(  ) {
    stroke(edgeColor);
    //strokeWeight(0.35);
    strokeWeight(weight);
    line(from.x, from.y, to.x, to.y);
  }
}


class Node {
  float x, y;
  float dx, dy;
  boolean fixed;
  String label;

  int count;

  public void increment(  ) {
    count++;
  }

  Node(String label) {
    this.label = label;
    x = random(width);
    y = random(height);
  }


  public void relax(  ) {
    float ddx = 0;
    float ddy = 0;

    for (int j = 0; j < nodeCount; j++) {
      Node n = nodes[j];
      if (n != this) {
        float vx = x - n.x;
        float vy = y - n.y;
        float lensq = vx * vx + vy * vy;
        if (lensq == 0) {
          ddx += random(1);
          ddy += random(1);
        } 
        else if (lensq < 100*100) {
          ddx += vx / lensq;
          ddy += vy / lensq;
        }
      }
    }
    float dlen = mag(ddx, ddy)/2;
    if (dlen > 0) {
      dx += ddx / dlen;
      dy += ddy / dlen;
    }
  }

  public void update(  ) {
    if (!fixed) {
      x += constrain(dx, -5, 5);
      y += constrain(dy, -5, 5);

      x = constrain(x, 0, width);// width*0.05, width*0.95);
      y = constrain(y, 0, height);//height*0.05, height*0.95);
    }
    dx /= 2;
    dy /= 2;
  }

  public void draw(  ) {
    //fill(nodeColor);
    noFill();
//    stroke(0);      
//    strokeWeight(0.5);

    //    if(count > textWidth(label)) {
    //fill(colorMap(count), 128);
    ellipse(x, y, count, count);
    
//    fill(0);
//    textAlign(CENTER, CENTER);
//    text(label, x, y);  
    //  } 
    //    else {
    //      ellipse(x, y, 6, 6);
    //    }
  }

  public int colorMap(int count) {
    return color(16*count, 128*count, 8*count, 255);
  }

}









  static public void main(String args[]) {
    PApplet.main(new String[] { "--bgcolor=#F0F0F0", "game_map2" });
  }
}
