import processing.core.*; 
import processing.data.*; 
import processing.event.*; 
import processing.opengl.*; 

import processing.serial.*; 
import static javax.swing.JOptionPane.*; 

import java.util.HashMap; 
import java.util.ArrayList; 
import java.io.File; 
import java.io.BufferedReader; 
import java.io.PrintWriter; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.io.IOException; 

public class Sinus_simulace extends PApplet {




float val_x;
float val_y;
float theta = 0.0f;  // Start angle at 0
float amplitude = 75.0f;  // Height of wave
float period = 200.0f;  // How many pixels before the wave repeats
float dx;  // Value for incrementing X, a function of period and xspacing
float[] yvalues;  // Using an array to store height values for the wave

int rectX = 500;
int rectY = 150;      // Position of square button
int rectSize_x = 80 ;     
int rectSize_y = 30;   
int currTimer=millis()+1000; 

int mi=0;//minutes
int se=0;//seconds
int start = 0;
int hodnota_x=350;
int hodnota_y=350;
int hox;
int hoy;
int rychlost = 50;
int xspacing = 8;   // How far apart should each horizontal location be spaced
int w=300;              // Width of entire wave
String s;
String rx="no Data";
boolean rectOver = false;
String nazevPortu;
String dispSec="";//string var to display seconds with leading zero

final boolean debug = true;

HScrollbar hs1, hs2;  // Two scrollbars
Serial myPort;        // The serial port

public void setup() {
  
 
  dx = (2*PI / period) * xspacing;
  yvalues = new float[w/xspacing];
  
  noStroke();
  
  hs1 = new HScrollbar(50,250, 300, 16, 16); //vyr\u00e1b\u00edme scrollbary :)
  hs2 = new HScrollbar(50,350, 300, 16, 16); //(bod_x,bod_y,d\u00e9lka,16,16)
  
  try_ports();
  
  thread("outcomming");
}

public void draw() {
  background(255);
  
  if(start == 1){
  casovac();
  }

  update();
  renderWave(); 
  ukaz_cas();
  
  //zpackan\u00fd scrollbary...
  float slider1Pos = hs1.getPos()-width/2; //
  float slider2Pos = hs2.getPos()-width/2; //
 
  hox = hodnota_x = PApplet.parseInt((slider2Pos+267)*2.34f);
  hoy = hodnota_y = 350-(PApplet.parseInt(val_y)*2);
  rychlost = PApplet.parseInt((slider1Pos+267)/4.25f)+30; 
  
  hs1.update();
  hs2.update();
  hs1.display();
  hs2.display();
  
  while (myPort.available() > 0) {
    String inBuffer = myPort.readString();   
    if (inBuffer != null) {
    rx = inBuffer;  
    }
  }
     
  stroke(255, 0, 0);
  noFill();
  rect(50,10,290,180);
  line(50,100,340,100);
  line(196,10,196,190);
  fill(153);
  rect(rectX, rectY, rectSize_x, rectSize_y);
  fill(0,255,0);
  rect(rectX, rectY+50, rectSize_x, rectSize_y);
  
  textSize(12);
  fill(0,0,255);
  text("0", 40, 105); 
  text("y", 40, 15); 
  text("x", 340, 205);  
  text(hoy, 550, 80);//vypisuju hodnoty xy
  text(hox, 550, 60);
  text(rx,450,40);   
  text(s,500,100);  //vypisuju out string
  
  fill(50);
  text("hodnota nastaveni motoru:",370,80);
  text("hodnota nastaveni serva:",370,60);
  text("Serial RX:",370,40);
  text("ASCII string:",370,100);
  
  textSize(16);
  text("Start",rectX+20,rectY+15);
  text(nazevPortu,rectX+20,rectY+65);
  text("Rychlost simulace:",50,235);
  text(rychlost,200,235);
  text("Natoceni servomotoru:",50,335); 

}
/** Example of selecting a particular Serial Port
 *  from the list of all possibilities returned
 *  in Serial.list().
 *
 *  Uses showInputDialog box kindly suggested to
 *  me by GoToLoop.  Thank you, GoToLoop! 
*/
  
public void try_ports() {
   String COMx, COMlist = "";
/*
  Other setup code goes here - I put this at
  the end because of the try/catch structure.
*/
  try {
    if(debug) printArray(Serial.list());
    int i = Serial.list().length;
    if (i != 0) {
      if (i >= 2) {
        // need to check which port the inst uses -
        // for now we'll just let the user decide
        for (int j = 0; j < i;) {
          COMlist += PApplet.parseChar(j+'a') + " = " + Serial.list()[j];
          if (++j < i) COMlist += ",  ";
        }
        COMx = showInputDialog("Which COM port is correct? (a,b,..):\n"+COMlist);
        if (COMx == null) exit();
        if (COMx.isEmpty()) exit();
        i = PApplet.parseInt(COMx.toLowerCase().charAt(0) - 'a') + 1;
      }
      String portName = Serial.list()[i-1];
      if(debug) println(portName);
      nazevPortu = portName;
      myPort = new Serial(this, portName, 115200); // change baud rate to your liking
      myPort.bufferUntil('\n'); // buffer until CR/LF appears, but not required..
    }
    else {
      showMessageDialog(frame,"Device is not connected to the PC");
      exit();
    }
  }
  catch (Exception e)
  { //Print the type of error
    showMessageDialog(frame,"COM port is not available (may\nbe in use by another program)");
    println("Error:", e);
    exit();
  }
}
class HScrollbar {
  int swidth, sheight;    // width and height of bar
  float xpos, ypos;       // x and y position of bar
  float spos, newspos;    // x position of slider
  float sposMin, sposMax; // max and min values of slider
  int loose;              // how loose/heavy
  boolean over;           // is the mouse over the slider?
  boolean locked;
  float ratio;

  HScrollbar (float xp, float yp, int sw, int sh, int l) {
    swidth = sw;
    sheight = sh;
    int widthtoheight = sw - sh;
    ratio = (float)sw / (float)widthtoheight;
    xpos = xp;
    ypos = yp-sheight/2;
    spos = xpos + swidth/2 - sheight/2;
    newspos = spos;
    sposMin = xpos;
    sposMax = xpos + swidth - sheight;
    loose = l;
  }

  public void update() {
    if (overEvent()) {
      over = true;
    } else {
      over = false;
    }
    if (mousePressed && over) {
      locked = true;
    }
    if (!mousePressed) {
      locked = false;
    }
    if (locked) {
      newspos = constrain(mouseX-sheight/2, sposMin, sposMax);
    }
    if (abs(newspos - spos) > 1) {
      spos = spos + (newspos-spos)/loose;
    }
  }

  public float constrain(float val, float minv, float maxv) {
    return min(max(val, minv), maxv);
  }

  public boolean overEvent() {
    if (mouseX > xpos && mouseX < xpos+swidth &&
       mouseY > ypos && mouseY < ypos+sheight) {
      return true;
    } else {
      return false;
    }
  }

  public void display() {
    noStroke();
    fill(204);
    rect(xpos, ypos, swidth, sheight);
    if (over || locked) {
      fill(0, 0, 0);
    } else {
      fill(102, 102, 102);
    }
    rect(spos, ypos, sheight, sheight);
  }

  public float getPos() {
    // Convert spos to be values between
    // 0 and the total width of the scrollbar
    return spos * ratio;
  }
}
public void calcWave() {//funkce po\u010d\u00edt\u00e1 sinus
  // Increment theta (try different values for 'angular velocity' here
  theta += 0.02f;

  // For every x value, calculate a y value with sine function
  float x = theta;
  for (int i = 0; i < yvalues.length; i++) {
    yvalues[i] = sin(x)*amplitude;
    x+=dx;
  }
}

public void renderWave() { //funkce vykresluje sinus
  noStroke();
  fill(0);
  // A simple way to draw the wave with an ellipse at each location
  for (int x = 0; x < yvalues.length; x++) {
    ellipse(x*xspacing+50, 100+yvalues[x], 5, 5);
  }
  fill(0,0,255);
  val_y = yvalues[(yvalues.length)/2];
  ellipse(196,val_y+100,8,8);
}
public void outcomming(){
  while(true){
  
    if(start == 1){
    calcWave(); 
    }
    else{
    hodnota_y = 350;
    }
  
    s = ("//A"+hodnota_x+"."+hodnota_y+"&"); //vytvo\u0159\u00edm outcomming string
    myPort.write(s);   
  
    if(hodnota_y <352 && hodnota_y >348){ //tohle je d\u016fle\u017eit\u00e9 pro zastaven\u00ed elektromotoru, jinak nepojede dozadu, kdy\u017e se na nule \u00fapln\u011b nazastav\u00ed. ZMRD!
    delay(500);
    }
  
    delay(rychlost); 
    }
}
public void casovac(){
 
  if(millis()>=currTimer){ //execute when millis reaches 1 second
    currTimer=millis()+1000; //reset to one second in the future
   se=se+1; //increment seconds
   if(se==60){ //check if 60 seconds passed
     se=0; //reset to 0
     mi=mi+1; //add one minute
   }
  }
}
  
public void ukaz_cas(){
  
  stroke(100);
  noFill();
  rect(400,250,200,75); //box showing time
 //display text inside box 
  
  fill(0);
  if (se<=9){ //check if seconds below 10 so we can prefix with leading zero
     dispSec="0" + se; //under 10 seconds, prefix display variable with zero and append seconds 
   } else { 
     dispSec= ""+ se; //no prefix needed, seconds are above 9
   }
 textSize(60);
  text(mi + ":" + dispSec,450,300);
  }
public void update() {
 if ( overRect(rectX, rectY, rectSize_x, rectSize_y) ) {
    rectOver = true;
  } else {
    rectOver = false;
  }
}

public void mousePressed() {
  if (rectOver) {
    if (start == 1){
        mi=0;
        se=0;
        dispSec="";
        start = 0;
      }else{
        currTimer=millis()+100;
        start = 1;
      }
    }
}

public boolean overRect(int xm, int ym, int width, int height)  {
  if (mouseX >= xm && mouseX <= xm+width && mouseY >= ym && mouseY <= ym+height) {
    return true;
  } else {
    return false;
  }
}
  public void settings() {  size(640, 480); }
  static public void main(String[] passedArgs) {
    String[] appletArgs = new String[] { "Sinus_simulace" };
    if (passedArgs != null) {
      PApplet.main(concat(appletArgs, passedArgs));
    } else {
      PApplet.main(appletArgs);
    }
  }
}
