import processing.core.*; 
import processing.xml.*; 

import java.applet.*; 
import java.awt.Dimension; 
import java.awt.Frame; 
import java.awt.event.MouseEvent; 
import java.awt.event.KeyEvent; 
import java.awt.event.FocusEvent; 
import java.awt.Image; 
import java.io.*; 
import java.net.*; 
import java.text.*; 
import java.util.*; 
import java.util.zip.*; 
import java.util.regex.*; 

public class gridding_subdiv_web extends PApplet {

//
// gridding off space available in the image plane
// repeatibility
// accuracy
// precision
//
// rtwomey@u.washington.edu
//
//---------------------------------------------------
// physical setup
float MOTORSEP = 96.0f;//72.0; // how far apart are the two steppers

// offsets
// line A, at limit switch, is 2.0 inches out. 
// line B, at limit switch, is 1.25 inches out. 

// paper size
float PAGEW = 66.0f;//86.0;//36.0;//17.0;//22.0;//44.0;  // width
float PAGEH = 48;//60.0;//14.0;//30;//30.0;  // height
float MARGIN = 6.0f;//12.0;

// page location relative to motors
float PAGE0X = (MOTORSEP-PAGEW)/2.0f;  // upper left
float PAGE0Y = 17.0f;//3.0;//12.0;//6.0-PAGEH;  // top of paper //12.0;//40.0;

// home position of pen carriage. where it is when the program starts
float HOMEA = 86.5f;//81.0;//82.0;// 58.5225650924496; // 108
float HOMEB = 86.5f;//81.0;//82.0;//56.02580320709378; // 108

// scale image to screen for display
int windowH = 900;
float onScreenScale=windowH/PAGEH;
int windowW = PApplet.parseInt(PAGEW*onScreenScale);

float MAXLENGTH=1.0f;

static int PEN_UP = 0;
static int PEN_DOWN = 1;

int WAITTIME = 0;//1500;

// limit of the algorithmic process
int MAXDEPTH=10;

int FORWARD = 0;
int BACKWARD= 1;

int START = 0;
int PATTERN_A=1;
int PATTERN_B=2;

//-----------------------------------
// state variables
boolean On = false;// USED SO THAT THE KEY 'S' AND 'G' START AND STOP THE DRAWING SEQUENCE, starts off to calibrate
boolean done = false;
boolean jumping = true;
boolean atPoint = true; // if the arduino returns that it is at the point, turn true.
boolean atHome=true;

int zoomLevel=0; // what zoom level
int x=0; // which grid x position
int y=0;  // which grid y position
int count=0; // which stroke
int s=0;  // which segment of stroke
int inc=1;


int state=START;
int dir=FORWARD;
float segs=0.0f; // how many segments in current line
boolean segDone=false;

float lx, ly; // fractional x,y positions
PVector Endpoint; // end of current line
PVector Last; // end of last line

// configuration
boolean serialOn = false;
boolean writeToFile = true;
boolean printToConsole = true;

// fast forward to doResume previous job
boolean doResume=false;
int runtoX=0;
int runtoY=3;
int runtoLine=2;
int runtoZoom=2;
float startX=65.20017f;
float startY=90.21675f;



//------------------------------------
// main program
public void setup() {
  // graphics
  size(1237,900);
  
  // load a font for text display
  PFont font;
  font = loadFont("Serif-24.vlw"); 
  textFont(font, 12); 

  background(255);

  // calc home values for X and Y (upper left corner of page)
  //  HOMEA = sqrt(pow(PAGE0X, 2)+pow(PAGE0Y, 2));
  //  HOMEB = sqrt(pow(MOTORSEP-(PAGE0X), 2)+pow(PAGE0Y, 2));
  if(doResume) {
    HOMEA = startX;
    HOMEB = startY;
  }
  
  if (printToConsole)
    println("(HOME: "+HOMEA + " " +HOMEB+")");

  Last = new PVector(HOMEA, HOMEB);
  Endpoint = new PVector(Last.x, Last.y);
  
  println("(at "+Last.x+","+Last.y+" relative to PAGE(0,0))");

  noCursor();
  
  On = true;

}


public void draw() {  
  stroke(120);
  strokeWeight(2);
  fill(220);

  if (On && !done) {
    if (drawGridAtScale(MARGIN, MARGIN, PAGEW-MARGIN, PAGEH-MARGIN, PApplet.parseInt(pow(2, zoomLevel)))) {
      if (incrementPosition(PApplet.parseInt(pow(2, zoomLevel))))
      {
        zoomLevel++;
        if (printToConsole)
          println("(grid "+zoomLevel+":"+MAXDEPTH+")");

        if (zoomLevel>MAXDEPTH) {
          done=true;
        }
      }
    }
    if (doneFFWDing())
      delay(WAITTIME);
  }
}

public boolean doneFFWDing() {
  if (!doResume)return true;

  if ((x==runtoX) && (y==runtoY) && (zoomLevel==runtoZoom) && (count==runtoLine)) {
    println("(found doResume point)");
    serialOn=true;
    doResume=false;
  }
  return false;
}

public boolean incrementLine(int scale) {
  y++;

  if (printToConsole)
    println("(line "+y+":"+zoomLevel+")");
  if (y>=scale) {
    y=0;
    return true;
  }
  return false;
}

public int toggleState(int state) {
  if (state==PATTERN_A)
    return PATTERN_B;
  return PATTERN_A;
}

public boolean incrementPosition(int scale) {

  if (state==START) {
    state=PATTERN_A;
    return true;
  }

  state = toggleState(state);

  if (dir==FORWARD) {
    x++;
    if (x>=scale)
    {
      x=scale-1;
      dir=BACKWARD;
      return incrementLine(scale);
    }
  } 
  else {
    x--;
    if (x<0) {
      x=0;
      dir=FORWARD;
      return incrementLine(scale);
    }
  }
  return false;
}

public boolean drawGridAtScale(float left, float top, float right, float bot, int scale) {

  if (state==START) {
    return drawFirstBox(MARGIN, MARGIN, PAGEW-MARGIN, PAGEH-MARGIN);
  } 
  else {

    float ux=(right-left)/(float)scale;
    float uy=(bot-top)/(float)scale;
    //println("(ux "+ux+" uy "+uy+" scale "+scale+" count "+count+")");
    
    if (dir==FORWARD) {
      switch (count) {
      case 0:
        if (state!=PATTERN_A)  
          drawScaledSegment(left+ux*x, top+uy*y, left+ux*(x+1), top+uy*(y+1));
        if (state!=PATTERN_B)
          drawScaledSegment(left+ux*x, top+uy*(y+1), left+ux*(x+1), top+uy*y);
        break;
      case 1:      
        drawScaledSegment(left+ux*(x+1), top+uy*(y+0.5f), left+ux*x, top+uy*(y+0.5f));
        break;
      case 2:
        if (state!=PATTERN_A)  
          drawScaledSegment(left+ux*(x+0.5f), top+uy*y, left+ux*(x+0.5f), top+uy*(y+1));
        if (state!=PATTERN_B)  
          drawScaledSegment(left+ux*(x+0.5f), top+uy*(y+1), left+ux*(x+0.5f), top+uy*y);

        break;
      };
    } 
    else {
      //BACKWARD
      switch (count) {
      case 0:
        if (state!=PATTERN_A)  
          drawScaledSegment(left+ux*(x+1), top+uy*(y+1), left+ux*x, top+uy*y);
        if (state!=PATTERN_B)
          drawScaledSegment(left+ux*(x+1), top+uy*y, left+ux*x, top+uy*(y+1));
        break;
      case 1:
        drawScaledSegment(left+ux*x, top+uy*(y+0.5f), left+ux*(x+1), top+uy*(y+0.5f));
        break;
      case 2:
        if (state!=PATTERN_A)  
          drawScaledSegment(left+ux*(x+0.5f), top+uy*(y+1), left+ux*(x+0.5f), top+uy*y);
        if (state!=PATTERN_B)  
          drawScaledSegment(left+ux*(x+0.5f), top+uy*y, left+ux*(x+0.5f), top+uy*(y+1));
        break;
      }
    }
    if (isLineDone()) {
     count++;
     println("(line "+(count-1)+" done. current line in pattern is line "+count+")");
     if (count>2) {
       println("(pattern "+x+","+y+":"+scale+" done )");
        count=0;
        return true;
      }
    }
  }
  return false;
}

public boolean drawFirstBox(float left, float top, float right, float bot) {
  float ux=(right-left);
  float uy=(bot-top);
  float x=0;
  float y=0;

  switch (count) {
  case 0:
    drawScaledSegment(left, top, right, top);
    break;
  case 1:
    drawScaledSegment(right, top, right, bot);
    break;
  case 2:
    drawScaledSegment(right, bot, left, bot);
    break;
  case 3:
    drawScaledSegment(left, bot, left, top);
    break;
  case 4:
    drawScaledSegment(left, top, right, bot);
    break;
  case 5:
    drawScaledSegment(right, top, left, bot);
    break;
  case 6:      
    drawScaledSegment(left, top+uy*0.5f, right, top+uy*0.5f);
    break;
  case 7:
    drawScaledSegment(left+ux*0.5f, top, left+ux*0.5f, bot);
    break;
  }

  if (isLineDone()) {
    count++;
    if (count>7) {
      count=0;
      return true;
    }
  }

  return false;

  //  count++;
  //  if (count>7) {
  //    count=0;
  //    return true;
  //  }
  //  return false;
}

public boolean isLineDone() {
  //println("(checking segment "+(s-1)+" of line "+count+", at pattern "+x+","+y+"...)");
  if (s>=segs){
    // finish multi-part line
    if(segs>1) {
      
      line(lx*onScreenScale, ly*onScreenScale, Endpoint.x*onScreenScale, Endpoint.y*onScreenScale);
      //println("finished long line");
      if (serialOn && !doResume) {  
//        machine.penDown();
//        machine.sendVal(Endpoint.x, Endpoint.y, PEN_DOWN);
//        machine.waitForOk();
      }
    };
    
    s=0;
    // store current position
    segDone=true;
    
    //Last=Endpoint;
        Last.x=Endpoint.x;
        Last.y=Endpoint.y;
    //println("(done.)");
    return true;
  }
  //println("(not done.)");
  return false;
}

public void drawScaledSegment(float x1, float y1, float x2, float y2) {

  if (s==0) {
    // starting a new line
    if (printToConsole)
      //println("("+s+":"+count+":"+x+","+y+":"+zoomLevel+")");
      println("("+zoomLevel+":"+x+","+y+":"+count+":"+s+")");

    // jog to a new position
    stroke(240, 0, 0, 24);
    line(Last.x*onScreenScale-2, Last.y*onScreenScale-2, x1*onScreenScale-2, y1*onScreenScale-2);
    if (serialOn && !doResume) {
//      machine.penUp();
//      machine.sendVal(x1, y1, PEN_UP);
//      machine.waitForOk();
    }
    //println("\tdid jog");
    
    segDone=false;
    float d = dist(x1, y1, x2, y2);
    if (d>MAXLENGTH) {
      // line length needs to be broken down
      lx=x1;
      ly=y1;
      segs = d/MAXLENGTH;
      s=1;
      Endpoint.x=x2;
      Endpoint.y=y2;
      //println("\tthis is a long line");
    } 
    else {
      // line length is ok, draw it
      stroke(0);
      line(x1*onScreenScale, y1*onScreenScale, x2*onScreenScale, y2*onScreenScale);
      //println("(this is a short line)");
      //println("\tdrew short line");
      if (serialOn && !doResume) {  
//        machine.penDown();
//        machine.sendVal(x2, y2, PEN_DOWN);
//        machine.waitForOk();
      }
      
      // signal we drew a line, and we are done
      s=1;
      segs=1;
      segDone=true;
      
      // store current position
      Endpoint.x=x2;
      Endpoint.y=y2;
      Last.x=x2;
      Last.y=y2;
    }
  }

  if ((s>0) && !segDone) { 
    // continuing a line segment
    if (printToConsole)
      println("("+zoomLevel+":"+x+","+y+":"+count+":"+s+")");

    // draw segment
    stroke(0);
    float tx=lerp(x1, x2, (float)s/segs);
    float ty=lerp(y1, y2, (float)s/segs);

    line(lx*onScreenScale, ly*onScreenScale, tx*onScreenScale, ty*onScreenScale);
    if (serialOn && !doResume) {  
//      machine.penDown();
//      machine.sendVal(tx, ty, PEN_DOWN);
//      machine.waitForOk();
    }
    //println("\tdrew segment");
    lx=tx;
    ly=ty;

    s++;
  }
}

public void drawCrossHairs(float x, float y) {
  float px, py;
  px=x*onScreenScale;
  py=y*onScreenScale;
  //  stroke(128, 0, 0, 128);
  //  line(px-3, py, px+3, py);
  //  line(px, py-3, px, py+3);

  line(px-5, py-5, px+5, py+5);
  line(px+5, py-5, px-5, py+5);
};

public void stop() {
//  machine.penUp();
//  machine.DirectCommand("G1 X"+HOMEA+" Y"+HOMEB+" Z0.0 F"+FASTFEED);
//  machine.stop();
};


// keyboard interface

public void keyPressed() {
  if (key == 'g') { 
    // start movement
    On = true;
    if (printToConsole)println("(GO!)");
//
//    machine.DirectCommand("G100 S100"); //moves servo, push pen off page
//    machine.waitForOk();
    jumping=true;
  }

  if (key == 's') {
    // stop movement
    On = false;
    if (printToConsole)println("(STOP)");
  }

  if (key=='h') {
    //machine.goHome();
  }

  if (!On) {
    switch(key) {
    case '1':
      // top left
      //machine.sendVal(0, 0, 0);
      //machine.sendVal(MARGIN, MARGIN, 0);
      break;
    case '2':
      // top right
      //machine.sendVal(PAGEW, 0, 0);
      //machine.sendVal(PAGEW-MARGIN, MARGIN, 0);
      break;
    case '3':
      // lower right
      //machine.sendVal(PAGEW, PAGEH, 0);
      //machine.sendVal(PAGEW-MARGIN, PAGEH-MARGIN, 0);
      break;
    case '4':
      // lower left
      //machine.sendVal(0, PAGEH, 0);
      //machine.sendVal(MARGIN, PAGEH-MARGIN, 0);
      break;      
    case '0':
      //machine.penUp();
      if (printToConsole)println("(going to stored home position)");
      //machine.DirectCommand("G1 X"+HOMEA+" Y"+HOMEB+" Z0.0 F"+FASTFEED);
      break;
    case 'd':
      //machine.penDown();
      break;
    case 'u':
      //machine.penUp();
      break;
    default:
      break;
    }
  }
}

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