/* ============================================
  code is placed under the MIT license
  Copyright (c) 2024 J-M-L
  For the Arduino Forum : https://forum.arduino.cc/u/j-m-l

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
  ===============================================
*/



#define DEBUG 1    // SET TO 0 OUT TO REMOVE TRACES

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#if DEBUG
#define D_SerialBegin(...) Serial.begin(__VA_ARGS__);
#define D_print(...)       Serial.print(__VA_ARGS__)
#define D_write(...)       Serial.write(__VA_ARGS__)
#define D_println(...)     Serial.println(__VA_ARGS__)
#else
#define D_SerialBegin(...)
#define D_print(...)
#define D_write(...)
#define D_println(...)
#endif


const byte boutonPin = 3;
const byte xPin = A0; // Broche de l'axe X de l'accéléromètre
const byte yPin = A1; // Broche de l'axe Y de l'accéléromètre
const byte zPin = A2; // Broche de l'axe Z de l'accéléromètre

Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire); // écran de 128x64 pixels

enum {ATTENTE, EN_VOL, PEUT_ETRE_A_TERRE, A_TERRE} etatActuel = ATTENTE;

unsigned long tempsDebut = 0;       // Variable pour stocker le temps au lancement
unsigned long tempsEcoule = 0;      // Variable pour stocker le temps écoulé
unsigned long tempsInactivite = 0;  // Variable pour mesurer la durée d'inactivité (début d'inactivité)
const unsigned long delaiInactivite = 300000ul; // 5 minutes en millisecondes
const int toleranceCapteur = 20;    // Tolérance pour considérer un changement significatif ~2%

inline void afficheTemps(const char * txt, unsigned long h, unsigned long m, unsigned long s) {
  display.clearDisplay();
  display.setCursor(0, 0);
  display.print(txt);     D_print(txt);
  display.print(h);       D_print(h);
  display.print(':');     D_write(':');
  if (m < 10) {
    display.print("0");   D_write('0');
  }
  display.print(m);       D_print(m);
  display.print(':');     D_write(':');
  if (s < 10) {
    display.print("0");   D_write('0');
  }
  display.print(s);       D_println(s);
  display.display();
}

void afficherTempsEcoule() {
  static unsigned long heurePrecedente = -1, minPrecedente = -1, secPrecedente = -1;
  tempsEcoule = millis() - tempsDebut;
  unsigned long secondes = tempsEcoule / 1000ul;
  unsigned long minutes = secondes / 60ul;
  unsigned long heures = minutes / 60ul;
  minutes  %= 60ul;
  secondes %= 60ul;

  if (secondes != secPrecedente || minutes != minPrecedente || heures != heurePrecedente) { // on affiche que si nécessaire
    afficheTemps("Vol ", heures, minutes, secondes);
    secPrecedente = secondes;
    minPrecedente = minutes;
    heurePrecedente = heures;
  }
}

void afficherDureeVol() {
  unsigned long duree = tempsInactivite - tempsDebut; // la durée effective
  unsigned long secondes = duree / 1000ul;
  unsigned long minutes = secondes / 60ul;
  unsigned long heures = minutes / 60ul;
  minutes  %= 60ul;
  secondes %= 60ul;
  afficheTemps("Duree ", heures, minutes, secondes);
}


void demarrerChronometre() {
  tempsDebut = millis();
  display.clearDisplay();
  display.setCursor(0, 0);
  display.println("EN VOL");
  display.display();
}

bool ballonEnVol() {
  static const unsigned long periodeEntreMesures = 1000; // on lit l'accéléromètre de temps en temps

  static unsigned long derniereMesure = -1000;
  static bool enVol = true;
  static int derniereLectureX = -1000;
  static int derniereLectureY = -1000;
  static int derniereLectureZ = -1000;

  if (millis() - derniereMesure >= periodeEntreMesures) { // est-ce le moment de faire une lecture ?
    // double lecture pour plus de stabilité
    analogRead(xPin);
    int x = analogRead(xPin);

    analogRead(yPin);
    int y = analogRead(yPin);

    analogRead(zPin);
    int z = analogRead(zPin);

    // Vérifier si les valeurs de l'accéléromètre ont changé de manière significative
    if (abs(x - derniereLectureX) < toleranceCapteur && abs(y - derniereLectureY) < toleranceCapteur && abs(z - derniereLectureZ) < toleranceCapteur) {
      enVol = false; // L'accéléromètre est inactif, on n'est plus en vol
    } else {
      // Mettre à jour les dernières lectures si le capteur est actif
      derniereLectureX = x;
      derniereLectureY = y;
      derniereLectureZ = z;
      enVol = true; // L'accéléromètre est actif, on est en vol
    }
    derniereMesure = millis();
  }
  return enVol;
}

void setup() {
  pinMode(boutonPin, INPUT_PULLUP);
  D_SerialBegin(115200);
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3D)) { // Addresse 0x3D pour 128x64, et 0x3C pour 128x32
    for (;;); // on s'arrête ici
  }
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.clearDisplay();

  display.setCursor(0, 0);
  display.print("PRET"); D_println("PRET");
  display.display();
}

void loop() {
  switch (etatActuel) {
    case ATTENTE:
      if (digitalRead(boutonPin) == LOW) {
        demarrerChronometre();
        etatActuel = EN_VOL;
      }
      break;

    case EN_VOL:
      if (! ballonEnVol()) {                // si le ballon n'est plus en vol
        D_println("INACTIF");
        tempsInactivite = millis();         // Enregistrer le temps du début d'inactivité
        etatActuel = PEUT_ETRE_A_TERRE;
      } else afficherTempsEcoule();
      break;

    case PEUT_ETRE_A_TERRE:
      if (millis() - tempsInactivite >= delaiInactivite) {
        afficherDureeVol();
        etatActuel = A_TERRE; // état confirmé
      }
      else if (ballonEnVol()) {
        D_println("ACTIF A NOUVEAU");
        etatActuel = EN_VOL; // Revenir à l'état "EN_VOL" si l'accéléromètre est à nouveau actif
      }
      break;

    case A_TERRE:
      yield(); // on en fait plus rien
      break;
  }
}