/*------------------------------------------------------------------------------------
   Program:     port_select

   Description: Allows a serial port to be selected and connected to graphically.
                Has the following buttons:
                Up         - scrolls up through the serial port list 
                Down       - scrolls down through the serial port list
                Connect    - connects to the selected serial port
                Disconnect - disconnects from the serial port allowing a new serial
                             port to be connected to
                Refresh    - refreshes the list of serial ports. Useful if a new
                             serial device is connected to the PC after this app-
                             lication is started
                
   Purpose:     Allows the serial port to be selected within an application instead
                of hard-coding the port number
                
   Hardware:    Can be used to connect to Arduino or other serial devices
   
   Software:    Developed using Processing 2.2.1 (processing.org)
                Uses the Button class from:
                http://blog.startingelectronics.com/a-simple-button-for-processing-language-code/
                
   Date:        21 July 2015   Author:   W.A. Smith, http://startingelectronics.org
   
------------------------------------------------------------------------------------*/

import processing.serial.*;
import interfascia.*;

GUIController c;
IFTextField tpause, tvalues, cmdstring; // textfields : pause time, X values, command string.
IFLabel lpause, lnext, lvalues, lcmd;         // labels of textfields

Serial serial_port = null;        // the serial port

// serial port buttons
Button btn_serial_up;              // move up through the serial port list
Button btn_serial_dn;              // move down through the serial port list
Button btn_serial_connect;         // connect to the selected serial port
Button btn_serial_disconnect;      // disconnect from the serial port
Button btn_serial_list_refresh;    // refresh the serial port list

// other buttons
Button btn_serial_STOP;
Button btn_serial_Home;
Button btn_serial_mup;              
Button btn_serial_mdn;
Button btn_serial_mlft;              
Button btn_serial_mrgt;
Button btn_next;
Button btn_sequence;

// Blinking alert messages
BlinkingText bkm_moving;
BlinkingText bkm_ready;
BlinkingText bkm_waiting;


String serial_list;                // list of serial ports
int serial_list_index = 0;         // currently selected serial port 
int num_serial_ports = 0;          // number of serial ports in the list
int refreshPeriod = 10;            // GUI refresh period in ms.

float posX;                          // X position
float posZ;                          // Z position

String inString;  // Input string from serial port
int lf = 10;      // ASCII linefeed

boolean isHoming = false;
boolean isHomingZ = false;
boolean isHomingX = false;
boolean isMoving = false;
boolean isSequence = false;
boolean isNext = false;
boolean isReady = false;
boolean isWaiting = false;
boolean isUp = false;
boolean isLeft = false;
boolean isConnected = false;
boolean hasGoals = false;

String action = "";
String result = "";

int posNum = 0;
int pauseDuration = 0;
int Xtargets[];
int panelColor = 218;
int lastRefresh = 0;
int waitStart, waitEnd;

void setup() {
  // set the window size
  size (640, 480);
  background(51);
  
  c = new GUIController(this);
  lpause = new IFLabel("Pause duration / seconds", 400, 220);
  tpause = new IFTextField("pause", 400, 236, 60, "");
  lvalues = new IFLabel("X positions list, in mm - Space separated - No . no , no ;", 152, 162);
  lnext = new IFLabel("Next goal:", 186, 220);
  tvalues = new IFTextField("xvalues", 152, 178, 474, "");
  lcmd = new IFLabel("Send command:", 420, 390);
  cmdstring = new IFTextField("cmdsrting", 420, 410, 100, "S");

  
  // create the buttons
  btn_serial_up = new Button("^", 340, 50, 40, 20);
  btn_serial_dn = new Button("v", 340, 80, 40, 20);
  btn_serial_connect = new Button("Connect", 500, 60, 100, 25);
  btn_serial_disconnect = new Button("Disconnect", 500, 90, 100, 25);
  btn_serial_list_refresh = new Button("Refresh", 500, 30, 100, 25);
  btn_serial_Home = new Button("Home", 20, 160, 100, 25);
  btn_serial_STOP = new Button("STOP", 100,400, 50, 25);
  btn_serial_mup = new Button("^", 40, 390, 40, 20);
  btn_serial_mdn = new Button("v", 40, 414, 40, 20);
  btn_serial_mlft = new Button("<", 170, 402, 40, 20);
  btn_serial_mrgt = new Button(">", 218, 402, 40, 20); 
  btn_next = new Button("GO NEXT", 186, 280, 80, 25); 
  btn_sequence = new Button("ALL SEQUENCE", 400, 280, 100, 25); 
  bkm_moving = new BlinkingText("MOVING", 4, 280, 80, 25, #FF0000);
  bkm_ready = new BlinkingText("READY", 4, 295, 80, 25, #00FF00);
  bkm_waiting = new BlinkingText("WAITING", 490, 280, 80, 25, #00FF00);
 
  // get the list of serial ports on the computer
  serial_list = Serial.list()[serial_list_index];
  
  //println(Serial.list());
  //println(Serial.list().length);
  
  // get the number of serial ports in the list
  num_serial_ports = Serial.list().length;
 
  c.add(tpause);
  c.add(lpause);
  c.add(tvalues);
  c.add(lvalues);
  c.add(lnext);
  c.add(cmdstring);
  c.add(lcmd);

  tpause.addActionListener(this);
  tvalues.addActionListener(this);
  cmdstring.addActionListener(this);
  
  
}

void mousePressed() {
  // up button clicked
  if (btn_serial_up.MouseIsOver()) {
    if (serial_list_index > 0) {
      // move one position up in the list of serial ports
      serial_list_index--;
      serial_list = Serial.list()[serial_list_index];
    }
  }
  // down button clicked
  if (btn_serial_dn.MouseIsOver()) {
    if (serial_list_index < (num_serial_ports - 1)) {
      // move one position down in the list of serial ports
      serial_list_index++;
      serial_list = Serial.list()[serial_list_index];
    }
  }
  // Connect button clicked
  if (btn_serial_connect.MouseIsOver()) {
    if (serial_port == null) {
      // connect to the selected serial port
      serial_port = new Serial(this, Serial.list()[serial_list_index], 115200);
      println("Info: connected");
      isConnected = ( serial_port != null );
      serial_port.bufferUntil(lf);
    }
  }
  // Disconnect button clicked
  if (btn_serial_disconnect.MouseIsOver()) {
    if (isConnected) {
      // disconnect from the serial port
      serial_port.stop();
      serial_port = null;
      isConnected = false;
    }
  }
  // Refresh button clicked
  if (btn_serial_list_refresh.MouseIsOver()) {
    // get the serial port list and length of the list
    serial_list = Serial.list()[serial_list_index];
    num_serial_ports = Serial.list().length;
  }
  // S button clicked
     if (isConnected) {
         if (btn_serial_STOP.MouseIsOver()) {
            serial_port.write("S\n");
            println("cmd: S");
            isSequence = false;
            isNext = false;
            isHoming = false;
         }
  }
  // Home button clicked
     if (isConnected) {
       if (btn_serial_Home.MouseIsOver()) {
          isHoming = true;
          isHomingZ = true;
          serial_port.write("IZ\n");
          println("cmd: IZ");
          } 
  }  
    
  // > button clicked
     if (isConnected) {
       if (btn_serial_mrgt.MouseIsOver()) {
          serial_port.write("+X\n");
          println("cmd: +X");
       }  
  }
  // < button clicked
     if (isConnected) {
       if (btn_serial_mlft.MouseIsOver()) {
          serial_port.write("-X\n");
          println("cmd: -X");
       }  
  }
  // ^ button clicked
     if (isConnected) {
       if (btn_serial_mup.MouseIsOver()) {
          serial_port.write("+Z\n");
          println("cmd: +Z");
       }  
  }
  // v button clicked
     if (isConnected) {
       if (btn_serial_mdn.MouseIsOver()) {
          serial_port.write("-Z\n");
          println("cmd: -Z");
       }  
  }
  // Proceed button clicked
  if ( isConnected && hasGoals ) {
       if (btn_sequence.MouseIsOver()) {
          isSequence = true;
          posNum = 0;
       }
  }


  // Next button clicked
  if ( isConnected && hasGoals ) {
       if (btn_next.MouseIsOver()) {
          isNext = true;
       }
  }
}


void serialEvent(Serial p) {
  if (isConnected) {
    inString = p.readString();
    serial_port.clear();  
    String[] q = split(inString, " ");
    inString = "";
//      println(q.length + " values found"); // debug info, no longer needed.
    switch(trim(q[0])) {
      case "R:": 
        action = trim(q[1]);
        result = trim(q[2]);
        println(action, result);
      break;
      case "K:":
        isUp = !boolean(int(q[1]));
        posX = float(q[2]);
        isLeft = (posX < 125 );
        posZ = float(q[3]);
        DisplayXZ(posX, posZ);
        isMoving = boolean(int(trim(q[4])));
      default:
      break;
     }
  }
}

void draw() {
    
  if( isWaiting ) {
    if (currentTime() < waitEnd ) {
      isWaiting = true;
    }
      else {
      isWaiting = false;
      println("waiting time elapsed");
    }
  } 
  
  if( hasGoals ) {
    hasGoals = posNum < Xtargets.length;
  }
  
  if( millis() - lastRefresh > refreshPeriod) {
     lastRefresh = millis();
     DrawPanelTitle("1 - USB setup", 4,4,632,120);
     // draw the text box containing the selected serial port
     DrawTextBox("Select Port:", serial_list, 10, 36, 320, 60);
     // draw the buttons in the first panel
     btn_serial_up.Draw(panelColor);
     btn_serial_dn.Draw(panelColor);
     btn_serial_connect.Draw(panelColor);
     
     if ( isConnected ) {
          btn_serial_disconnect.Draw(#00FF00);
     } else {
          btn_serial_disconnect.Draw(panelColor);
     }
     btn_serial_list_refresh.Draw(panelColor);
  
     DrawPanelTitle("2 - Activate", 4,128,140,200);
     
     if( isConnected && !( isUp && isLeft )) {
            btn_serial_Home.Draw(#00FF00);
     }
     else {
            btn_serial_Home.Draw(panelColor);
     }
     
     DisplayXZ(posX, posZ); 
     DisplayEndStop(isUp);
 
     bkm_moving.Set(isMoving);
     bkm_moving.Draw();

     bkm_ready.Set(isReady);
     bkm_ready.Draw();

     DrawPanelTitle("3 - Automated moves", 148,128, 488, 200);
     
     if( hasGoals ) {
        fill(panelColor);
        rect(186, 220, 40, 20);
        fill(0);
        textAlign(LEFT);
        textSize(12);
        text(str(Xtargets[posNum]), 186, 240, 40, 20); 
     }
     
     if ( hasGoals && isConnected && !isSequence ) {
       btn_next.Draw(#00FF00);
     } else {
       btn_next.Draw(panelColor);
     }
  
     if ( hasGoals && isConnected && !isSequence && (pauseDuration > 10) ) {
       btn_sequence.Draw(#00FF00);
     } else {
       btn_sequence.Draw(panelColor);
     }
  
     bkm_waiting.Set(isWaiting);
     bkm_waiting.Draw();

     DrawPanelTitle("4 - Manual commands - use with caution", 4,334,632,138);
     btn_serial_STOP.Draw(#FF0000);
     btn_serial_mup.Draw(panelColor);
     btn_serial_mdn.Draw(panelColor);
     btn_serial_mrgt.Draw(panelColor);
     btn_serial_mlft.Draw(panelColor);
  }
  
  if( isHoming && isHomingZ && isUp) {
    isHomingZ = false;
    isHomingX = true;
    serial_port.write("GX 120\n");
  }
  
  if( isHoming && isHomingX && (action.equals("MX")) && (result.equals("success"))) {
    isHomingX = false;
    isHoming = false;
    println("Homing completed");
  }
  
 
  
  if( isNext && !isMoving && isUp ) {
    String cmdToSend = "GX " + str(Xtargets[posNum]) + "\n";
    println(cmdToSend);
    serial_port.write(cmdToSend);
  }  
  
    if( isNext && (action.equals("MX")) && (result.equals("success"))) {
    println("received MX success: going down");
    action = "";
    result = "";
    serial_port.write("Z 40\n");
  } 
  
    if( isNext && (action.equals("MZ")) && (result.equals("success")) ) {
    println("received MZ success: ready");
    action = "";
    result = "";
    isReady = true;
    isNext = false;
  }   
  
   if( isNext && !isMoving && !isUp ) {
        serial_port.write("IZ\n");
  }
  
  if( isSequence && hasGoals && !isMoving && !isWaiting && isUp) {   
    String cmdToSend = "GX " + str(Xtargets[posNum]) + "\n";
    println(cmdToSend);
    serial_port.write(cmdToSend);
    posNum++;
  }
  
  if( isSequence && (action.equals("MZ")) && (result.equals("success")) && !isUp) {
    println("received MZ success: ready");
    action = "";
    result = "";
    isReady = true;
  } 
  
  if( isSequence && isReady && !isWaiting ) {
    waitStart = currentTime();
    waitEnd = waitStart + pauseDuration;
    isWaiting = true;
    isReady = false;
    println("started waiting");
  }

  if( isSequence && (action.equals("MX")) && (result.equals("success")) && !isHoming) {
    println("received MX success: going down");
    action = "";
    result = "";
    serial_port.write("Z 40\n");
  }
  
  if( isSequence && (posNum >= Xtargets.length)) {
    println("Sequence completed");
    isSequence = false;
  }
  
  if( isSequence && (posNum < Xtargets.length) && !isMoving && !isWaiting && !isUp) {
    serial_port.write("IZ\n");
  }
}


// function for drawing a panel with the title
void DrawPanelTitle(String title, int x, int y, int w, int h)
{
  fill(panelColor);
  rect(x, y, w, h);
  fill(0);
  textAlign(CENTER);
  textSize(14);
  text(title, x + 10, y+10, w - 20, 28); 
}

// function for drawing a text box with title and contents
void DrawTextBox(String title, String str, int x, int y, int w, int h)
{
  fill(240);
  rect(x, y+16, w, h - 10);
  fill(64);
  textAlign(LEFT);
  textSize(12);
  text(title, x + 10, y, w - 20, h - 10);
  textSize(12);  
  text(str, x + 10, y + 20, w - 20, h - 10);
}

// button class used for all buttons
class Button {
  String label;
  float x;    // top left corner x position
  float y;    // top left corner y position
  float w;    // width of button
  float h;    // height of button
  
  // constructor
  Button(String labelB, float xpos, float ypos, float widthB, float heightB) {
    label = labelB;
    x = xpos;
    y = ypos;
    w = widthB;
    h = heightB;
  }
  
  // draw the button in the window
  void Draw(int ink) {
    fill(ink);
    stroke(0);
    rect(x, y, w, h, 10);
    textAlign(CENTER, CENTER);
    fill(0);
    text(label, x + (w / 2), y + (h / 2));
  }
  
  // returns true if the mouse cursor is over the button
  boolean MouseIsOver() {
    if (mouseX > x && mouseX < (x + w) && mouseY > y && mouseY < (y + h)) {
      return true;
    }
    return false;
  }
}


void actionPerformed (GUIEvent e) {
  if ((e.getSource() == tvalues )&&(e.getMessage() == "Completed")) {
          Xtargets = int(splitTokens(tvalues.getValue(), " "));
          println("Info: Number of positions: ",Xtargets.length); 
          for (int i = 0; i<Xtargets.length ; i++) {
            print(" ",Xtargets[i]);
          }
          if (Xtargets.length > 0) {
            posNum = 0;
            hasGoals = true;
          }
          println("");
          
  }
  if ((e.getSource() == tpause )&&(e.getMessage() == "Completed")) {
          pauseDuration = int(tpause.getValue());
          println("Pause duration: ",pauseDuration);
  }
  
  if ((e.getSource() == cmdstring )&&(e.getMessage() == "Completed")) { 
      println("cmd: " + cmdstring.getValue());
      serial_port.write(cmdstring.getValue() + "\n");
      switch(cmdstring.getValue()) {
        case "IZ": 
          isHomingZ = true;
        case "IX":
          isHomingX = true;
        break;
        default:
        break;
      }
  }
}


void DisplayXZ(float valx, float valz) {
   DrawTextBox("PosX: ", str(valx), 10, 240, 60, 30);
   DrawTextBox("DistZ: ", str(valz), 80, 240, 60, 30);
}

void DisplayEndStop(boolean state) {
   textAlign(LEFT);
   textSize(12);
   text("Upper end stop : ", 18, 210, 90, 20);
   color c = color(255*int(state),255*(1-int(state)),0); 
   fill(c);
   rect(108, 210, 10,10);
}

int currentTime() {
   int current_s = second();
   int current_m = minute();
   int current_h = hour();
   int current = 3600*current_h + 60*current_m + current_s;
   return current;
}


class BlinkingText {
  String label;
  float x;    // top left corner x position
  float y;    // top left corner y position
  float w;    // width of button
  float h;    // height of button
  boolean a;  // active
  color c;    // color
  
  // constructor
  BlinkingText(String labelB, float xpos, float ypos, float widthB, float heightB, color highlightC) {
    label = labelB;
    x = xpos;
    y = ypos;
    w = widthB;
    h = heightB;
    a = false; 
    c = highlightC;
  }
  
  void Set(boolean state) {
    a = state;
  }
  
  void Draw() {
    if(a) {
      fill(panelColor);
      if( second() % 2 == 1 ) {
                  fill(c);
                } else {
                  fill(0);
                }
      text(label, x + (w / 2), y + (h / 2));
     }
     else {
      fill(panelColor);
      stroke(panelColor);
      rect(x + (w / 2), y, w, h);
     }
  }
}