// import des librairies
#include <ESP8266WiFi.h>
#include <TimeLib.h>                  // https://github.com/PaulStoffregen/Time
#include <NtpClientLib.h>             // https://github.com/gmag11/NtpClient  nécessite https://github.com/me-no-dev/ESPAsyncUDP

#define MySSID "XXXX"                 // Changez votre reseau Wifi
#define MyWifiPassword "YYYY"         // Changez votre mot de passe reseau Wifi  

#define NTP_TIMEOUT 1500              //  TIMEOUT de la connexion NTP


int8_t timeZone = 1;                                    // GMT (paris) 
int8_t minutesTimeZone = 0;                             // pas utile ici, reste a zero
const PROGMEM char *ntpServer = "fr.pool.ntp.org";      // le serveur ntp qu'on intérroge, cela peut étre votre serveur si vous en avez un.
bool wifiFirstConnected = false;             // flag de connexion
unsigned long previousMillis = 0;            // variable pour la fonction millis    
const long interval = 1000;                  // interval pour la fonction millis


// fonction de connexion 
void onSTAConnected (WiFiEventStationModeConnected ipInfo) {    // lorsque l'on est connecté     
    Serial.printf ("Connecté à %s\r\n", ipInfo.ssid.c_str ());  // on affiche le SSID
}

// fonction de connexion bis
void onSTAGotIP (WiFiEventStationModeGotIP ipInfo) {                                        // lorsque l'on a une IP      
    Serial.printf ("IP: %s\r\n", ipInfo.ip.toString ().c_str ());                           // on l'affiche
    Serial.printf ("Connecté: %s\r\n", WiFi.status () == WL_CONNECTED ? "oui" : "non");     // on vérifie la connexion  
    wifiFirstConnected = true;                                                              // on active le flag de première connexion
}

// fonction de deconnexion
void onSTADisconnected (WiFiEventStationModeDisconnected event_info) {        // si on est déconnecté           
    Serial.printf ("Déconnecté du SSID: %s\n", event_info.ssid.c_str ());     // on l'affiche
    Serial.printf ("Erreur: %d\n", event_info.reason);                        // avec le code erreur
    NTP.stop();                                                               // on stop la synchro au serveur NTP
    WiFi.reconnect ();                                                        // on lance la reconnexion au wifi
}

// fonction de traitement des event avec le serveur NTP
void processSyncEvent (NTPSyncEvent_t ntpEvent) {                            
    if (ntpEvent < 0) {                                                      // Si on retourne une erreur, suivant celle-ci on affiche le problème   
        Serial.printf ("Erreur de synchronisation: %d\n", ntpEvent);
        if (ntpEvent == noResponse)
            Serial.println ("Serveur NTP injoignable");
        else if (ntpEvent == invalidAddress)
            Serial.println ("Addresse du serveur NTP invalide");
        else if (ntpEvent == errorSending)
            Serial.println ("Echec d'envoi de la requète");
        else if (ntpEvent == responseError)
            Serial.println ("Pas de réponse du serveur NTP");
    } else {                                                                // si le serveur NTP nous repond correctement
        if (ntpEvent == timeSyncd) {
            Serial.print ("Récupération du temps sur le serveur NTP: ");    // on affiche l'heure récupéré
            Serial.println (NTP.getTimeDateString (NTP.getLastNTPSync ()));
        }
    }
}

boolean syncEventTriggered = false;  // flag de connexion au serveur NTP (Vrai si un événement a été déclenchée)
NTPSyncEvent_t ntpEvent;             // Dernier événement déclenché

// fonction pour mettre 0 devant les nombres de 0 a 9, pour être sur 2 chiffres.
void printDigits(int digits){ 
  if(digits < 10) {
    Serial.print('0');
  }
  Serial.print(digits);
}

// fonction qui affiche l'heure
void digitalClockDisplay(){  
  printDigits(hour());
  Serial.print(":");
  printDigits(minute());
  Serial.print(":");
  printDigits(second());
  Serial.println();
}

// fonction qui affiche la date 
void digitalDateDisplay(){  
  printDigits(day());
  Serial.print("/");
  printDigits(month());
  Serial.print("/");
  printDigits(year());
  Serial.println();
}


// le setup 
void setup () {
    static WiFiEventHandler e1, e2, e3;    // on déclare 3  event sur le wifi: e1 e2 et e3
    Serial.begin(115200);                  // on démarre la com série 
    Serial.println();
    WiFi.mode (WIFI_STA);                               // on definie le mode de Wifi 
    WiFi.begin (MySSID, MyWifiPassword);                // on demarre la connexion au réseau Wifi     
    // on definie notre synchro
    NTP.onNTPSyncEvent ([](NTPSyncEvent_t event) {      // quand un evenement se produit
          ntpEvent = event;                             // on definie nos event                              
          syncEventTriggered = true;                    // on passe le flag syncEventTriggered a vrai 
      });  
    e1 = WiFi.onStationModeGotIP (onSTAGotIP);                 // la fonction de connexion bis  est associé a l'evenement Wifi e1
    e2 = WiFi.onStationModeDisconnected (onSTADisconnected);   // la fonction de déconnexion est associé a l'evenement Wifi e2
    e3 = WiFi.onStationModeConnected (onSTAConnected);         // la fonction de connexion est associé a l'evenement Wifi e3
}


// la boucle
void loop () {      
  if (wifiFirstConnected) {                                               // quand le flag de la première connexion est activé
      wifiFirstConnected = false;                                         // on le bascule a false
      NTP.setInterval (63);                                               // on régle l'interval de synchonisation en seconde  
      NTP.setNTPTimeout (NTP_TIMEOUT);                                    // on regle le timout de synchonisation
      NTP.begin (ntpServer, timeZone, true, minutesTimeZone);             // on démarre la syncho avec le serveur NTP toute les 63s
  }
  
  if (syncEventTriggered) {                                              // des qu'une synchro est efféctuée
      processSyncEvent (ntpEvent);                                       // on lance la fonction qui traite le retour de la synchro (erreur ou réussite)
      syncEventTriggered = false;                                        // on repasse le flag syncEventTriggered a false 
  }

  unsigned long currentMillis = millis();                                // variable currentMillis vos millis ( le temps depuis le debut du programme)                     
  if (currentMillis - previousMillis >= interval) {                      // des qu'on dépasse ou atteint l'interval (donc ici toute les secondes) 
    previousMillis = currentMillis;                                      // on reset notre compteur temps
    Serial.println("******************************");                    // on affiche 
    Serial.println (WiFi.isConnected () ? "connecté" : "non connecté");           // si on est connecté
    Serial.println (NTP.isSummerTime () ? "Summer Time. " : "Winter Time. ");     // si on est sur l'heure d'été ou d'hiver
    digitalDateDisplay();                                                         // on affiche la date 
    digitalClockDisplay();                                                        // on affiche l'heure
    Serial.println("******************************");
  }
}