////////  USER INTERFACE  /////////////////////////////////////////////////
void adjustCoordinates(int x, int y) {
  scrnX  = Math.round( x - WORLD_SIZE_W/2 );
  scrnY  = Math.round( y - WORLD_SIZE_H/2 );
  pointX = Math.round( scrnX/factor );
  pointY = Math.round( scrnY/factor );
}

void doSelectNode() {
  //func = "select node";
  Iterator it = nodes.entrySet().iterator(); 
  selection = null;
  while (it.hasNext ()) {
    Map.Entry e = (Map.Entry)it.next();
    String k  = (String) e.getKey();
    Node n = (Node) e.getValue();  
    //if (debug) print(n.name + "\t");
    if ( (! "MARK".equals(n.shape)) && pointerOverRect(n.x, n.y, n.w, n.h) ||
		"MARK".equals(n.shape) && pointerOverRect(n.x + n.w/factor/2, n.y, n.w, n.h) ) {
      //func = "select node";
      sel_edge = null;
      selection = n;
      if ( selection == null || 
          previous_selection == null ||  
          ! previous_selection.id.equals(selection.id) ) {
        if (nodetrace) println("NEW SELECTION "+ selection.name);
        previous_selection = selection;
        if (javascript != null) javascript.new_select(selection.id, selection.proj);
      }
      //sel_edge = null;
      selectNode(selection);
      //selected  = null;
    }    
  }
  //ADDED BY IRI
  if (javascript != null) javascript.mousemove(selection);
  //
}

void doSelectEdge() {
  //func = "select edge";
  if (selection == null) {
    Iterator it = edges.entrySet().iterator(); 
    sel_edge = null;
    while (it.hasNext ()) {
      Map.Entry ie = (Map.Entry)it.next();
      String k  = (String) ie.getKey();
      Edge e = (Edge) ie.getValue();
      if (pointerOverLine(e.from, e.to) && e.dup==0) {
        //func = "select edge";
        //if (debug) println("select edge line "+e.asc_id);
        sel_edge = e;
      }
      else if (pointerOverArc( e )) {
        //func = "select edge";
        //if (debug) println("select edge arc  "+e.asc_id);
        sel_edge = e;      
      }
    }
  }
}

void move_vizgroup(int dx, int dy) {
  if (vizgroup!=null){
    for (int i = 0; i < vizgroup.size(); i++) {
      Node n = (Node) vizgroup.get(i);
      int originalX = n.x;
      int originalY = n.y;
      n.position(originalX + dx, originalY + dy);
    }  
  }
}

void doUpdateNodePosition() {
  //func =  "update node position";
  Iterator it = nodes.entrySet().iterator(); 
  while (it.hasNext ()) {
    Map.Entry e = (Map.Entry)it.next();
    String k  = (String) e.getKey();
    Node n = (Node) e.getValue(); 
    n.x += pointX - pressStartX;
    n.y += pointY - pressStartY;
    n.position(n.x, n.y);
  }
}

boolean pointerOverRect( float x, float y, float w, float h ) {
  int pX, pY;
  if (!isMouse) {
    pX = touchX; 
    pY = touchY;
  }
  else {
    pX = mouseX; 
    pY = mouseY;
  }
  adjustCoordinates(pX, pY);
  boolean isOver = false;
  if ( pointX >= x - w/factor/2 && pointX <= x + w/factor/2 && 
    pointY >= y - h/factor/2 && pointY <= y + h/factor/2 ) {
    isOver = true;
  } 
  return isOver;
}

boolean pointerOverLine( Node from, Node to ) {
  int pX, pY;
  if (!isMouse) {
    pX = touchX; 
    pY = touchY;
  }
  else {
    pX = mouseX; 
    pY = mouseY;
  }
  adjustCoordinates( pX, pY );
  boolean isOver  = false;
  Vector2D pFrom  = new Vector2D(from.x, from.y);
  Vector2D pTo    = new Vector2D(to.x, to.y);
  Vector2D pointer = new Vector2D(pointX, pointY);
  float distance = pointer.distance(pFrom, pTo);
  if (distance < 2) {
    isOver = true;
  } 
  return isOver;
}

boolean pointerOverArc( Edge e ) {
  //if (e.dup==0) return false;
  boolean isOver  = false;
  int ptX, ptY;
  float sigma = 0.1;
  if (!isMouse) {
    ptX = touchX; 
    ptY = touchY;
  }
  else {
    ptX = mouseX; 
    ptY = mouseY;
  }
  adjustCoordinates( ptX, ptY );
  // based on screen coordination. center(0,0) rightbottom(WoORLD_SIZE_W/2, WORLD_SIZE_H/2)
  if (e.from==null || e.to==null || e.centr==null) return false;
  fromX = factor * (e.from.x);
  fromY = factor * (e.from.y);
  toX   = factor * (e.to.x);
  toY   = factor * (e.to.y);
  cX    = factor * (e.centr.x);
  cY    = factor * (e.centr.y);
  PVector P    = new PVector(scrnX, scrnY);
  PVector Cntr = new PVector(cX, cY);
  if (P != null)  phai   = e.theta(Cntr, P);
  PVector P1   = new PVector(fromX, fromY);
  if (P1 != null) theta1 = e.theta(Cntr, P1);
  PVector P2   = new PVector(toX, toY);
  if (P2 != null) theta2 = e.theta(Cntr, P2);
  PVector T = PVector.sub(P, Cntr);
  float D   = dist(P1.x, P1.y, Cntr.x, Cntr.y);
  T.normalize(); 
  T.mult(D);
  T.add(Cntr);
  T.sub(P);
  float w = 0;
  if (sqrt(sq(T.x) + sq(T.y)) < 3) {
    if (edgetrace) println("initial theta1="+round(degrees(theta1))+" phai="+round(degrees(phai))+" theta2="+round(degrees(theta2)));
    if (-TWO_PI<theta1 && theta1<0) theta1 += TWO_PI;
    if (-TWO_PI<theta2 && theta2<0) theta2 += TWO_PI;
    if (-TWO_PI<phai && phai<0) phai += TWO_PI;
    float df = theta2 - theta1;
    if (-TWO_PI<df && df<0) df += TWO_PI;
    if (df > PI) {
      w      = theta2;
      theta2 = theta1;
      theta1 = w;
    }
    if((( -TWO_PI < phai-theta1 && phai-theta1 < -PI) ||
        ( 0 < phai-theta1 && phai-theta1 < PI)) &&
       (( -TWO_PI < theta2-phai && theta2-phai < -PI) ||
        ( 0 < theta2-phai && theta2-phai < PI)))
      isOver = true;
  }
  return isOver;
}

void doBeginMenu() {
  //func =  "begin menu";
  menu_active = true;
  menuX = scrnX;
  menuY = scrnY;
  selected = selection;
  selection = null;
  previous_selection = null;
  //javascript.end_select();
}

void doMenu() {
  if (debug) println( "menu=" + nls(menu,ln) );
  switch (menu) {
    case ROOT:
      rootNode(selected); 
      break;
    case HIDE:
      if (!selected.root) hideNode(selected);
      break;
    case INFO:
      infoNode(selected);
      break;
    case NEWREL:
      newRelation(selected);
      break;
    case SQUEEZE:
      squeezeNode(selected);
  }
  menu_active = false;
  selected  = null;
  dragging = false;
}

void doBeginEdgMenu() {
  //func =  "begin edge menu";
  edgmenu_active = true;
  menuX = scrnX;
  menuY = scrnY;
  selected_edge = sel_edge;
}

void doEdgMenu() {
  if (edgetrace) println( "edge menu=" + nls(edgmenu,ln) );
  switch (edgmenu) {
    case HIDEEDGE:
      hideEdge(sel_edge);
      break;
    case EDITEDGE:
      infoEdge(sel_edge); 
  }
  edgmenu_active = false;
  sel_edge = null;
  dragging = false;
}

void doBeginApMenu() {
  //func =  "begin ap menu";
  apmenu_active = true;
  menuX = scrnX;
  menuY = scrnY;
}

void doApMenu() {
  //func =  "execute ap menu";
  switch (apmenu) {
    case FITALL:
      fitAll();
      break;
    case ALLFIX:
      allFix();
      break;
    case ALLFREE:
      allFree();
      break;
    case GROUPSHAPE:
      groupShape();
      break;
    case ALLSAVE:
      allSave();
      break;
    case ALLRETRIEVE:
      allRetrieve();
      break;
    case NEWTOPIC:
      newTopic();
      break;
    case PEDIA:
      pedia();
      break;
    case MODE:
      setMode();
  }
  apmenu_active = false;
}

void setMode() {
  if (debug) println("setMode");
  if (javascript!=null) {
    String  lang = ln;
    boolean d = debug,
            p = personal,
            u = showusername,
            ed = editmode;
    String  b = both;
    javascript.set_mode( lang, d, p, u, b, ed );
  } 
}

void saveMode(String lang, String d, String p, String u, String b, String ed){
  if (debug) println("\t"+lang+" "+d+" "+p+" "+u+" "+b+" "+ed);
  if (lang!=null) ln = lang;
  if ("debug".equals(d)) {
    debug=true; 
    nodetrace=true;
    edgetrace=true;
  }
  else {
    debug=false;
    nodetrace=false;  
    edgetrace=false;
  }  
  if ("personal".equals(p)) 
    personal=true; else personal=false;  
  if ("showusername".equals(u)) 
    showusername=true; else showusername=false;  
  if ("edit".equals(ed)) {
    editmode=true; 
  }
  else {
    editmode=false;
  }
  both = b;
  if (debug) println("\tdebug="+debug+" personal="+personal+" showusername="+showusername+" both="+both+" editmode="+editmode);
}

void doExpandNode(Node node) {
//  funcTrace("Expandng...");
  boolean isfree=true;
  if(node.fixed) isfree=false;
  node.fix();
  expandNode(node, both); // "" means one direction "both" means bidirection expansion
  selection = null;
  previous_selection = null;
}

void doFixNode(Node node) {
  if (debug) println("doFixNode " + node.name);
  //func =  "fix node";
  node.fix();
  selection = null;
  previous_selection = null;
}

void doFreeNode(Node node) {
  if (debug) println("doFreeNode " + node.name);
  //func =  "free node";
  node.free();
  selection = null;
  previous_selection = null;
}

void doDragNode() {
  //func =  "drag node";
  draggingnode = true;
  int dx = pointX - selection.x;
  int dy = pointY - selection.y;
  selection.fix();
  //if (debug) println("mouseDragged " + selection.name + "\t(" + originalX + ",\t" + originalY + ") to (" + pointX + ",\t" + pointY + ")");
  if (selection.vizgroup == null) {
    selection.position(pointX, pointY);
  } else {
    move_vizgroup(dx, dy);
  }
  damper = 1.0;
}

////////  MENU  ///////////////////////////////////////////////////////////
boolean isOverMenu(float x, float y, int w, int h) {
  int pX, pY;
  if (!isMouse) {
    pX = touchX; 
    pY = touchY;
  }
  else {
    pX = mouseX; 
    pY = mouseY;
  }
  adjustCoordinates(pX, pY);
  if ((scrnX) >= x - w/2 && (scrnX) <= x + w/2 &&
      (scrnY) >= y - h/2 && (scrnY) <= y + h/2) {
    return true;
  } 
  else {
    return false;
  }
}

Vector2D adjustMenuPosition(int x, int y, int w, int h, int rows) {
  float WORLD_HALF_SIZE_W = (WORLD_SIZE_W - w)/2;
  float WORLD_HALF_SIZE_H = (WORLD_SIZE_H - h)/2;
  float new_x = x;
  float new_y = y;
  if ( x < -WORLD_HALF_SIZE_W )            new_x = -WORLD_HALF_SIZE_W;
  if ( x >  WORLD_HALF_SIZE_W )            new_x =  WORLD_HALF_SIZE_W;
  if ( y < -WORLD_HALF_SIZE_H )            new_y = -WORLD_HALF_SIZE_H;
  if ( y >  WORLD_HALF_SIZE_H - rows * h ) new_y =  WORLD_HALF_SIZE_H - rows * h;
  Vector2D newPosition = new Vector2D(new_x, new_y);
  return newPosition;
}

void showMenu() {
  stroke(Silver);
  strokeWeight(0.1);
  int w = 100;
  int h = 25;
  Vector2D menuPosition;
  float x0, y0;
  if (editmode) {
    menuPosition = adjustMenuPosition( menuX, menuY, w, h, 5);
    x0 = menuPosition.x;
    y0 = menuPosition.y;
    int i = 0;
    menuItem(x0, y0 + h*i++, w, h, nls(INFO,ln) );  
    menuItem(x0, y0 + h*i++, w, h, nls(ROOT,ln) );
    menuItem(x0, y0 + h*i++, w, h, nls(SQUEEZE,ln) );
    menuItem(x0, y0 + h*i++, w, h, nls(HIDE,ln) );
    menuItem(x0, y0 + h*i++, w, h, nls(NEWREL,ln) );
    if (isOverMenu(x0, y0, w, h)) {
      menuHighlite(     x0, y0,   w, h, nls(INFO,ln) );    menu = INFO;  } 
    else if (isOverMenu(x0, y0 + h, w, h)) {
      menuHighlite(     x0, y0 + h, w, h, nls(ROOT,ln) );    menu = ROOT;  }  
    else if (isOverMenu(x0, y0 + h*2, w, h)) {
      menuHighlite(     x0, y0 + h*2, w, h, nls(SQUEEZE,ln) ); menu = SQUEEZE;  } 
    else if (isOverMenu(x0, y0 + h*3, w, h)) {
      menuHighlite(     x0, y0 + h*3, w, h, nls(HIDE,ln) );    menu = HIDE;  } 
    else if (isOverMenu(x0, y0 + h*4, w, h)) {
      menuHighlite(     x0, y0 + h*4, w, h, nls(NEWREL,ln) );  menu = NEWREL;  }  
    else {
      menu        = 0;    menu_active = false;    selected    = null;  }
  }
  else {
    menuPosition = adjustMenuPosition( menuX, menuY, w, h, 2);
    x0 = menuPosition.x;
    y0 = menuPosition.y;
    int i = 0;
    menuItem(x0, y0 + h*i++, w, h, nls(INFO,ln) );  
    menuItem(x0, y0 + h*i++, w, h, nls(ROOT,ln) );
    if (isOverMenu(     x0, y0,       w, h)) {
      menuHighlite(     x0, y0,       w, h, nls(INFO,ln) );    menu = INFO; } 
    else if (isOverMenu(x0, y0 + h*1, w, h)) {
      menuHighlite(     x0, y0 + h*1, w, h, nls(ROOT,ln) );    menu = ROOT;  } 
    else {
      menu        = 0;    menu_active = false;    selected    = null;  }    
  }
}

void showEdgMenu() {
  if (menu_active || apmenu_active) return;
  stroke(Silver);
  strokeWeight(0.1);
  int w = 100;
  int h = 25;
  Vector2D menuPosition;
  float x0, y0;
  if (editmode) {
    menuPosition = adjustMenuPosition( menuX, menuY, w, h, 2 );
    x0 = menuPosition.x;
    y0 = menuPosition.y;  
    menuItem(x0, y0,       w, h, nls(EDITEDGE,ln) );
    //menuItem(x0, y0 + h,   w, h, nls(HIDEEDGE,ln) );
    if (isOverMenu(     x0, y0,     w, h)) {
      menuHighlite(     x0, y0,     w, h, nls(EDITEDGE,ln) ); edgmenu = EDITEDGE;  } 
    /*
    else if (isOverMenu(x0, y0 + h, w, h)) {
      menuHighlite(     x0, y0 + h, w, h, nls(HIDEEDGE,ln) ); edgmenu = HIDEEDGE;  } 
    */
    else {
      edgmenu        = 0;    edgmenu_active = false;    selected_edge  = null;  
    }
  }
}

void showApMenu() {
  if (menu_active) return;
  stroke(Silver);
  strokeWeight(0.1);
  int w = 100;
  int h = 25;
  Vector2D menuPosition;
  float x0, y0;
  if (editmode) {
    menuPosition = adjustMenuPosition( menuX, menuY, w, h, 9 );
    x0 = menuPosition.x;
    y0 = menuPosition.y;  
    int i = 0;
    menuItem(x0, y0 + h*i++, w, h, nls(FITALL,ln) );
    menuItem(x0, y0 + h*i++, w, h, nls(NEWTOPIC,ln) );  
    menuItem(x0, y0 + h*i++, w, h, nls(PEDIA,ln) );
    menuItem(x0, y0 + h*i++, w, h, nls(ALLFREE,ln) );
    menuItem(x0, y0 + h*i++, w, h, nls(ALLFIX,ln) );
    menuItem(x0, y0 + h*i++, w, h, nls(GROUPSHAPE,ln) );
    menuItem(x0, y0 + h*i++, w, h, nls(ALLSAVE,ln) );
    menuItem(x0, y0 + h*i++, w, h, nls(ALLRETRIEVE,ln) );
    menuItem(x0, y0 + h*i++, w, h, nls(MODE,ln) );
    if (isOverMenu(     x0, y0 + h*0, w, h)) {
      menuHighlite(     x0, y0 + h*0, w, h, nls(FITALL,ln) );     apmenu = FITALL;  } 
    else if (isOverMenu(x0, y0 + h*1, w, h)) {
      menuHighlite(     x0, y0 + h*1, w, h, nls(NEWTOPIC,ln) );   apmenu = NEWTOPIC;  } 
    else if (isOverMenu(x0, y0 + h*2, w, h)) {
      menuHighlite(     x0, y0 + h*2, w, h, nls(PEDIA,ln) );      apmenu = PEDIA;  } 
    else if (isOverMenu(x0, y0 + h*3, w, h)) {
      menuHighlite(     x0, y0 + h*3, w, h, nls(ALLFREE,ln) );    apmenu = ALLFREE;  } 
    else if (isOverMenu(x0, y0 + h*4, w, h)) {
      menuHighlite(     x0, y0 + h*4, w, h, nls(ALLFIX,ln) );     apmenu = ALLFIX;  }
    else if (isOverMenu(x0, y0 + h*5, w, h)) {
      menuHighlite(     x0, y0 + h*5, w, h, nls(GROUPSHAPE,ln) ); apmenu = GROUPSHAPE;  } 
    else if (isOverMenu(x0, y0 + h*6, w, h)) {
      menuHighlite(     x0, y0 + h*6, w, h, nls(ALLSAVE,ln) );    apmenu = ALLSAVE;  } 
    else if (isOverMenu(x0, y0 + h*7, w, h)) {
      menuHighlite(     x0, y0 + h*7, w, h, nls(ALLRETRIEVE,ln)); apmenu = ALLRETRIEVE;  } 
    else if (isOverMenu(x0, y0 + h*8, w, h)) {
      menuHighlite(     x0, y0 + h*8, w, h, nls(MODE,ln));        apmenu = MODE;  } 
    else {
      apmenu = 0;    apmenu_active = false;  }
  }
  else {
    menuPosition = adjustMenuPosition( menuX, menuY, w, h, 3 );
    x0 = menuPosition.x;
    y0 = menuPosition.y;  
    int i = 0;
    menuItem(x0, y0 + h*i++, w, h, nls(FITALL,ln) );
    menuItem(x0, y0 + h*i++, w, h, nls(PEDIA,ln) );
    menuItem(x0, y0 + h*i++, w, h, nls(MODE,ln) );
    if (isOverMenu(     x0, y0 + h*0, w, h)) {
      menuHighlite(     x0, y0 + h*0, w, h, nls(FITALL,ln) );     apmenu = FITALL;  } 
    else if (isOverMenu(x0, y0 + h*1, w, h)) {
      menuHighlite(     x0, y0 + h*1, w, h, nls(PEDIA,ln) );      apmenu = PEDIA;  } 
    else if (isOverMenu(x0, y0 + h*2, w, h)) {
      menuHighlite(     x0, y0 + h*2, w, h, nls(MODE,ln));        apmenu = MODE;  } 
    else {
      apmenu = 0;    apmenu_active = false;  }
  }
}

void menuItem(float x, float y, int w, int h, String name) {
  fill(White);
  rectMode(CENTER);
  rect(x, y, w, h);
  fill(0);
  textAlign(CENTER, CENTER);
  textSize(12);
  text(name, x, y);
}

void menuHighlite(float x, float y, int w, int h, String name) {
  fill(192);
  rectMode(CENTER);
  rect(x, y, w, h);
  fill(White);
  textAlign(CENTER, CENTER);
  textSize(12);
  text(name, x, y);
}

void fitAll() {
  //func =  "fit all";
  float minX=width/2, maxX=-width/2, minY=height/2, maxY=-height/2;
  float area_width, area_height, area_centerX, area_centerY;
  selection = null;
  previous_selection = null;
  //javascript.end_select();
  Iterator it = nodes.entrySet().iterator(); 
  while (it.hasNext ()) {
    Map.Entry e = (Map.Entry)it.next();
    String k  = (String) e.getKey();
    Node n = (Node) e.getValue();
    if (n.x < minX) minX = n.x;
    if (n.x > maxX) maxX = n.x;
    if (n.y < minY) minY = n.y;
    if (n.y > maxY) maxY = n.y;
  }  
  area_width  = maxX - minX;
  area_height  = maxY - minY;
  area_centerX = minX + area_width/2;
  area_centerY = minY + area_height/2;
  //if (debug) print("width=" + width + "\theight=" + height + "\tX\tmin=" + minX + "\tmax=" + maxX + "\tY\tmin=" + minY + "\tmax=" + maxY + "\n");
  //if (debug) print("Area\twidth=" + area_width + "\theight=" + area_height + "\tcentr\tx=" + area_centerX + "\ty=" + area_centerY + "\n");

  Iterator it2 = nodes.entrySet().iterator(); 
  while (it2.hasNext ()) {
    Map.Entry e = (Map.Entry)it2.next();
    String k  = (String) e.getKey();
    Node n = (Node) e.getValue();   
    n.x -= area_centerX;
    n.y -= area_centerY;
    n.position(n.x, n.y);
  }
  pressStartX = 0;
  pressStartY = 0;
  float sw = width/area_width, sh = height/area_height;
  float smin = (sw<sh)? sw:sh;
  float smax = (sw<sh)? sh:sw;
  scl = smin;
  if (scl > 4)  scl = 3.2;
  else          scl *= 0.8; 
  if (debug) print("scl="+scl+"\tmin="+smin+"\tmax="+smax+"\n");
  zoom(scl);
  if (javascript != null) javascript.setscale(scl);
}

String nls(int i, String ln) {
  if (ln.equals("ja")){
    switch(i) {
  // menu
      case EXPND:      return "展開";
      case ROOT:       return "中心ノード";
      case HIDE:       return "非表示";
      case INFO:       return "情報";
      case NEWREL:     return "関係定義";
      case SQUEEZE:    return "折りたたみ";
  // apmenu
      case FITALL:     return "全体表示";
      case ALLFIX:     return "全粘着";
      case ALLFREE:    return "全開放";
      case GROUPSHAPE: return "グループの形";
      case ALLSAVE:    return "退避";
      case ALLRETRIEVE: return "回復";
      case NEWTOPIC:   return "新規トピック";      
      case MODE:       return "実行モード";
      case PEDIA:      return "世界大百科事典";
  // edgemenu
      case HIDEEDGE:   return "非表示";      
      case EDITEDGE:   return "情報";      
      case DELETEEDGE: return "削除";
    }      
  }
  else {
    switch(i) {
  // menu
      case EXPND:      return "EXPAND";
      case ROOT:       return "ROOT";
      case HIDE:       return "HIDE";
      case INFO:       return "INFO";
      case NEWREL:     return "NEWREL";
      case SQUEEZE:    return "SQUEEZE";
  // apmenu
      case FITALL:     return "FIT ALL";
      case ALLFIX:     return "ALL FIX";
      case ALLFREE:    return "ALL FREE";
      case GROUPSHAPE: return "GROUP SHAPE";
      case ALLSAVE:    return "SAVE";
      case ALLRETRIEVE: return "RETRIEVE";
      case NEWTOPIC:   return "NEW TOPIC";
      case MODE:       return "MODE";
      case PEDIA:      return "ENCYCLOPEDIA";
  // edgemenu
      case HIDEEDGE:   return "HIDE EDGE";      
      case EDITEDGE:   return "EDGE INFO";      
      case DELETEEDGE: return "DELETE EDGE";
    }
  }
  return null;
}

