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

import processing.serial.*; 

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 centrifugeuse_processing extends PApplet {

/* *********************************************
Cyril Vallet - 2020 - www.sciencesmania.com   
Si vous modifiez/copier ce code merci d'indiquer
la provenance en mettant un lien vers mon site. Merci. 
 
Pour les montages et l'électronique voir mon site
www.sciencesmania.com  
********************************************* */



// creation des variables boutons
Bouton  boutonQuitter, boutonWeb, boutonCalibreBas,boutonCalibreHaut,boutonCapturer;

// pour les polices de caratères
PFont font20, font12,font15, font10, font25;

// après la connexion réussie les étapes : régagles / communication arduino / mesure de la durée
int etape=0; 

// pour changer l'icone de l'executable aller dans processing-3.5.4\modes\java\application
// et changer sketch.ico ! c'est tout !

public void setup(){
  
 //size(1024,768,P2D);
 
 
  // chargement d'une police sympa pour les boutons et autres -> présente dans le dossier data (à la racine)
  font10 = loadFont("Arial-BoldMT-10.vlw");
  font12 = loadFont("Arial-BoldMT-12.vlw");
  font15 = loadFont("Arial-BoldMT-15.vlw");
  font20 = loadFont("Arial-BoldMT-20.vlw");
  font25 = loadFont("Arial-BoldMT-25.vlw");
  
  // charger les images en mémoire
  chargerImages();
  
  // ************** Initialiation des BOUTONS - class Bouton **************
  // new bouton(x, y, largeur, hauteur, texte, fonction, style)
  boutonQuitter =  new Bouton(75, 150, 60, 25, "Quitter", "quitterFonction",1);
  boutonWeb =  new Bouton(75, height-13, 130, 25, "site", "webFonction",2);
  boutonCalibreBas = new Bouton(270,600, 180, 25, "Diminuer l'échelle du temps", "calibreBasFonction",1);
  boutonCalibreHaut = new Bouton(465,600, 180, 25, "Augmenter l'échelle du temps", "calibreHautFonction",1);
  boutonCapturer = new Bouton(700,600, 150, 25, "Capturer la courbe", "capturerFonction",1);

  // etablir la connexion avec l'Arduino 
  // ATTENTION Il qu'il n y ait QUE l'arduino de brancher sur les ports COM -> si pb
  connexionArduino();
}

public void draw(){
  background(50);
  
  // creation du fond 
  // barre verticale à gauche et l'image de la carte arduino  
  stroke(0); strokeWeight(1);
  fill(30);  rect(0, 0, 150, height);
  image(imageArduino, 20,20);
  
  // Nom du TP dans la barre à gauche
  fill(200,200,200);textFont(font20);
  text("        T.P.    \n Centrifugeuse", 10, height/2); 
  
 // afficher les FPS
    fill(80,80,80);textFont(font10);
    text(PApplet.parseInt(frameRate), 5, 10); 

  boutonQuitter.represente();

  // copyright
  //  boutonWeb.represente(); le bouton n'est pas representé mais le lien fonctionne !
    fill(100,100,100);textFont(font10);
    text("Cyril Vallet (c) - 2020 \n www.sciencesmania.com", 5, height-18); 
    
  
  // *******************************************************
  // **********  gestion de la connexion à l'arduino********
  //********************************************************

  if (etatConnexionArduino == -1){
    pbConnexion();
  }
  
   if (etatConnexionArduino == 1){
    fill(255,255,255);  textFont(font10); 
    text("Connexion à l'arduino ...", 25, 105);
    maConnexionArduino.write("g");  // envoie g pour voir si l'arduino la recoit et renvoie g (g pour go ;-))) )
  }
  
   if (etatConnexionArduino== 2){
    // écrire connecté en vet ! en dessous l'image de la carte Arduino
    fill(0,255,150);textFont(font10); 
    text("Connecté", 25, 105); 
    
    dessinerQuadrillage();
    
    boutonCalibreBas.represente();
    boutonCalibreHaut.represente();
    boutonCapturer.represente();
  
  } 
  
  
  // *******************************************************
  // **********  Connexion ok on passe au programme
  //********************************************************
  
  // la connexion est réussie on passe 
// d'abord au réglage puis aux mesures
  if (etatConnexionArduino== 2 && etape==0) {
    // l'arduino n'envoie encore rien comme données
    maConnexionArduino.write('r'); // ordre d'envoie la tension 
    etape =1;
  }
  

  if (etatConnexionArduino==2 && etape==1){
     afficherDonnees();
  
  }
  
  

}
float tableauMesure[] ={};
int stopMesure=0;
float echelleTemps=0.480f;

// pour la detection horizontal du clic de souris pour la mesure de période
int x1=-2;
int x2=-2; // -2 pour ne pas voir les droites verticales de la mesure au début.
int mesureOk=0;

public void afficherDonnees() {
  
  
  // tracer  la courbe si les données sont présentes
  if (tableauMesure.length > 299) {

  //  **************************
  //  etat de la centrifugeuse conditionné à partir des donées
  // *************************
    //let t=0;
    //while (tableauMesure[t] < 100 && t < 299){
    //  t++;  
    //}
    //if (t<299) etatCentrifugeuse=1; // la centrifugeuse tourne car tension > 100
    //if (t==299) {
    //  t=0;
    //  while (tableauMesure[t] < 25 && t < 299){
    //    t++;
    //  }
    //  if (t<299) {
    //    etatCentrifugeuse=2; // branchée mais tourne pas
    //  }else {
    //    etatCentrifugeuse=3; // non branchée
    //  }

    //}
      
  // ********************
  // tracé de la courbe
  //**********************
 
    for (int i=0; i<299; i++){
      stroke(0,255,0);  
      line(2*i+180, 550-tableauMesure[i]/2,2*(i+1)+180, 550-tableauMesure[i+1]/2);
      
      //petit rond pour la mesure
      stroke(0,255,0);
      strokeWeight(3);
      point(2*i+180,550-tableauMesure[i]/2);
      strokeWeight(1);  
    }  
        
    if (stopMesure==0) {

      // on remet le tableau à zero 
       //tableauMesure = [];  NE FONCTIONNE PAS ???
      tableauMesure = subset(tableauMesure, 0,0);
      // on demande à nouveau d'envoyer les données
      if (echelleTemps==1*0.030f)maConnexionArduino.write('r'); 
      if (echelleTemps==2*0.030f)maConnexionArduino.write('t');
      if (echelleTemps==4*0.030f)maConnexionArduino.write('u');
      if (echelleTemps==8*0.030f)maConnexionArduino.write('v');
      if (echelleTemps==16*0.030f)maConnexionArduino.write('w');
    }

  } 
  
  //****************
  // mesure de la PERIODE  
  //*********************
  if (stopMesure ==1){
    
    // expliquer le fonctionnement
    strokeWeight(0); fill(170); textFont(font12);
    text("Cliquer dans le cadre pour \neffectuer une mesure de durée", 620, 650);
    
    
    // dessiner 2 lignes verticales
    // que l'on peut déplacer en haut de la fenetre
    // au départ les deux lignes sont à x =-2 pour ne pas les voir
    strokeWeight(1); stroke(255);
    line(x1, 25,x1, 550); 
    line(x2, 25,x2, 550);

    fill(255);textFont(font15);;
    
    if (x1>0 && x2>0) {
      // ligne horizontale blanche
      strokeWeight(1); stroke(255);
      line(x1, 30, x2, 30);    
      
      // bien positionner la double flèche representant la périod
      int positionPeriode =0;
      if (x1 > x2) {
        positionPeriode = x2 + 20;
        line(x1, 30, x1-5, 25); line(x1, 30, x1-5, 35); // fleches
        line(x2, 30, x2+5, 25); line(x2, 30, x2+5, 35);
      } else {
        positionPeriode = x1 + 20;
        line(x2, 30, x2-5, 25); line(x2, 30, x2-5, 35); // fleches
        line(x1, 30, x1+5, 25); line(x1, 30, x1+5, 35);
      }
      
      strokeWeight(0); stroke(255);
    
    // écriture de la période avec un nombre après la virgule différent selon l'échantillonage
      if (echelleTemps > 4*0.030f){
        text(nfc((abs(x2-x1)*echelleTemps/2),2),positionPeriode, 20);
      }else {
        text(nfc((abs(x2-x1)*echelleTemps/2),2),positionPeriode, 20);
      }
      text("ms", positionPeriode+45, 20);
      
    // écrire l'incertitude
      fill(100); textFont(font10);  
      float incertitude=0;
        if (echelleTemps ==1*0.030f) incertitude =0.04f;  
        if (echelleTemps ==2*0.030f) incertitude =0.08f;    
        if (echelleTemps ==4*0.030f) incertitude =0.15f;    
        if (echelleTemps ==8*0.030f) incertitude =0.3f;    
        if (echelleTemps ==16*0.030f) incertitude =0.5f;  
      text("Incertitude : \n+/-  " + incertitude + "  ms",800, 20);
  
    }

  }

}

public void  mousePressed(){
  
  // si on clique dans le cadre de la courbe
  // après avoir appuyé sur CAPTURER LA COURBE
  // on enregistre la position du clic pour les mesures
  if (mouseX >179 && mouseX < 779 && mouseY>38 && mouseY<550){
    if (mesureOk ==0){
      x1 = mouseX;
      mesureOk = 1;
    }
    if (mesureOk==2) {
      x2= mouseX;
      mesureOk =3;
    }
    
  }
}
PFont font;


class Bouton {
  
  int bx;
  int by;
  int bw;
  int bh;
  String btext;
  String bfonction;
  int bstyle;
  
  // La position du bouton est en mode CENTER !
  // x position du bouton horizontale , y verticale, w largeur totale, h hauteur totale, 
  // text : texte du bouton , fonction : la fonction associée
  // style pour l'apparence
  Bouton(int x,int y, int w, int h, String text, String fonction,  int style){
    bx=x;
    by=y;
    bw=w;
    bh=h;
    btext =text;
    bfonction = fonction;
    bstyle = style;
  }
  
  //void actualise(){
  //  bx++;
  //  if (bx>width) bx=0;
  //}
  
  public void represente(){
    rectMode(CENTER);
    
    if (mouseX > bx-bw/2 && mouseX < bx+bw/2 && mouseY > by-bh/2 && mouseY < by+bh/2){
      
      if (mousePressed) {
         stroke(255);fill(1,1,1); strokeWeight(1);
         rect(bx, by, bw,bh,5);
         noStroke();
        }
       else {
         stroke(255);fill(67,181,73); strokeWeight(1);
         rect(bx, by, bw,bh,5);
         noStroke();
       }
      // cursor(HAND);
    } else {
    
    
    if (bstyle == 1) { stroke(255);fill(116,116,116); strokeWeight(1);}
    if (bstyle == 2) { stroke(255);fill(150,50,50); strokeWeight(1);}
    rect(bx, by, bw,bh,5);
     noStroke();
     //cursor(ARROW);
   
    }
    
     rectMode(CORNER);
     
    fill(255,255,255); textFont(font12);textAlign(CENTER);
    text(btext, bx, by +12/2-2);
    textAlign(CORNER);
    
  }
  
  public String checkOver() {
    if (mouseX > bx-bw/2 && mouseX < bx+bw/2 && mouseY > by-bh/2 && mouseY < by+bh/2){
    // println(bx);
     return bfonction;
    } else {
     return "";
    }
  }
  
  
}

public void mouseReleased(){
  
  if (stopMesure==1) {
    if (mesureOk==1)  mesureOk =2;
     if (mesureOk==3)  mesureOk =0;
     
  }

  String quelleFonction ;
  
    quelleFonction =  boutonWeb.checkOver();
    if (quelleFonction.equals("webFonction")){
      link("https://www.sciencesmania.com");
    }
    
  quelleFonction =  boutonQuitter.checkOver();
    if (quelleFonction.equals("quitterFonction")){
      exit();
    }
  
  quelleFonction =  boutonCalibreBas.checkOver();
    if (quelleFonction.equals("calibreBasFonction")){
       if (stopMesure==0){  
        echelleTemps =echelleTemps *2;
      if (echelleTemps > 0.480f) echelleTemps = 0.480f;
      
      if (echelleTemps ==1*0.030f) maConnexionArduino.write('r');  
      if (echelleTemps ==2*0.030f) maConnexionArduino.write('t');  
      if (echelleTemps ==4*0.030f) maConnexionArduino.write('u');    
      if (echelleTemps ==8*0.030f) maConnexionArduino.write('v');  
      if (echelleTemps ==16*0.030f) maConnexionArduino.write('w');
  }  
    } 
     quelleFonction =  boutonCalibreHaut.checkOver();
    if (quelleFonction.equals("calibreHautFonction")){
      if (stopMesure==0){
    
      echelleTemps=  echelleTemps/2;
      if (echelleTemps < 0.030f) echelleTemps = 0.030f;
      
      if (echelleTemps ==1*0.030f) maConnexionArduino.write('r');  
      if (echelleTemps ==2*0.030f) maConnexionArduino.write('t');  
      if (echelleTemps ==4*0.030f) maConnexionArduino.write('u');    
      if (echelleTemps ==8*0.030f) maConnexionArduino.write('v');  
      if (echelleTemps ==16*0.030f) maConnexionArduino.write('w');
      }  
    } 
    
      quelleFonction =  boutonCapturer.checkOver();
    if (quelleFonction.equals("capturerFonction")){
      if (stopMesure ==0){  
        stopMesure =1;
        boutonCapturer.btext = "Relancer les mesures";
        
        x1=-2;
        x2=-2;
      } else { 
        stopMesure=0;
        boutonCapturer.btext = "Capturer la courbe";  
      }
    } 
    


}
public void dessinerQuadrillage() {
  
  // dessiner le fond quadrillage
    stroke(20);strokeWeight(1);  fill(60);
    textSize(10);
    // cadre externe
    rect(179,38,600,512);
    // l'échelle des volts
    for (int i=0;i<5; i++){
      stroke(20,50);strokeWeight(1);
      line(175,550-102*i,775,550-102*i);
      fill(100);
      text(i + " V", 160, 550-102*i);
    }  
    
    // dessiner le quadrillage
    int grad=1;
    int nbPixel = PApplet.parseInt(2/echelleTemps);
    int v=nbPixel;
    while( nbPixel< 600) {
      stroke(20,50);strokeWeight(1);
      line(180+nbPixel,38,180+nbPixel,550);
      
      if (echelleTemps > 0.060f){
        if (grad%10==0) text(grad , 180+nbPixel, 560);
      } else {
        text(grad , 180+nbPixel, 560);
      }
      grad++;
      nbPixel = v + nbPixel;
    }
    
    // en bas à droite du quadrillage indiquer l'unite du temps
    strokeWeight(0); fill(255);textSize(10);
    text(" ms", 800, 560);
    
  
}

PImage imageArduino;


public void chargerImages() 
{
 imageArduino = loadImage("arduinosmall.png");

}
public void pbConnexion() { 

  fill(255, 80, 0);   textFont(font25);
  text("Problème de connexion \navec la carte Arduino !", 175, 50);

  fill(150, 150, 150);    textFont(font15);
  text("- Vérifier qu'il n'y a QUE la carte arduino de branchée sur l'ordinateur.\n\n- Fermer ce logiciel \n\n- Débrancher puis brancher à nouveau la carte Arduino.\n\n- Relancer ce logiciel.\n\n\n\n- Si le problème persiste, revoir précisement la procédure \ndans la partie fabrication du Guitayourt sur www.sciencesmania.com", 200, 150);
}

Serial maConnexionArduino;

int etatConnexionArduino =0;
String messageRecu;


int tension;

public void connexionArduino() {
  
  // si la liste des ports n'est pas nulle
  if (Serial.list().length > 0){
    
    // créer la connexion avec l'arduino
    maConnexionArduino = new Serial(this, Serial.list()[0], 2000000);  
    maConnexionArduino.bufferUntil(13); //met en stoke jusqu'à un retour chariot
    
    etatConnexionArduino =1; // une connexion au port est ok mais est-ce l'arduino avec le bon programme ?
    
  } else {
    etatConnexionArduino =-1;  // arduino non connecté  à l'ordinateur ?
  }
 
}

//**************************************
//          CAPTER LES EVENEMENTS SERIES
// ************************************

public void serialEvent(Serial p)
{

  
  // un port a été trouvé et processing s'y est connecté il faut vérifier que c'est l'arduino bien programmé
   if (etatConnexionArduino ==1) {   
     String messageRecu = trim(maConnexionArduino.readStringUntil(13)); // 13 c'est le retour chariot et le trim l'enlève
     if (messageRecu.equals("g")) etatConnexionArduino =2;  
   }
   
   // recupere le niveau de tension
   if (etatConnexionArduino ==2 && etape==1) {  
     String messageRecu = trim(maConnexionArduino.readStringUntil(13)); // 13 c'est le retour chariot et le trim l'enlève
     int tension = PApplet.parseInt(messageRecu);
     tableauMesure =append(tableauMesure, tension);
     
  }
  
  

}
  public void settings() {  fullScreen(); }
  static public void main(String[] passedArgs) {
    String[] appletArgs = new String[] { "centrifugeuse_processing" };
    if (passedArgs != null) {
      PApplet.main(concat(appletArgs, passedArgs));
    } else {
      PApplet.main(appletArgs);
    }
  }
}
