#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
#include "RTClib.h"

RTC_DS3231 rtc;

const int hours_switch          = D5;
const int minutes_switch        = D6;
const int max_wifi_attempts     = 10;
const int brightnessUpdateDelay = 2000;

unsigned long brightnessLastUpdate = 0;
 
int hours_offset   = 0;
int minutes_offset = 0;
int the_hours      = 0;
int the_minutes    = 0;

unsigned oldDigit3 = 0;
unsigned oldDigit2 = 0;
unsigned oldDigit1 = 0;
unsigned oldDigit0 = 0;

// Offset en heures entre l'heure UTC et l'heure locale (pour la France : +1 heure en hiver, +2 heures en été)
const long utc_offset = 1;

// Config Wifi
const char* ssid     = "[WIFI_SSID]";
const char* password = "[WIFI_PASSWORD]";
WiFiUDP ntp_UDP;

// Serveur NTP
NTPClient time_client(ntp_UDP, "europe.pool.ntp.org", utc_offset * 60 * 60);

// Affichage
Adafruit_7segment matrix = Adafruit_7segment();

void setup()
{

  // Initiatlisation de l'affichage
  matrix.begin(0x70);
  matrix.setBrightness(5);
  matrix.clear();
  matrix.writeDisplay();

  // Démarrage RTC
  if (!rtc.begin())
    abort();

  // Connexion Wifi
  int wifi_connection_retries = 0;
  WiFi.begin(ssid, password);
  while ((WiFi.status() != WL_CONNECTED) && (wifi_connection_retries < max_wifi_attempts))
  {
    wifi_connection_retries++;
    matrix.writeDigitNum(2, 0x10);
    matrix.writeDisplay();
    delay(250);
    matrix.clear();
    matrix.writeDisplay();
    delay(250);
  }

  // Reset de l'affichage
  matrix.clear();
  matrix.writeDisplay();

  // Variables pour l'initialisation du module RTC
  int current_day;
  int current_month;
  int current_year;
  int current_hours;
  int current_minutes;
  int current_seconds;
    
  // Si connexion wifi ok
  if (WiFi.status() == WL_CONNECTED)
  {
    
    // Initialisation du serveur NTP
    time_client.begin();
    time_client.update();
    
    // Mise à jour de l'heure
    unsigned long epochTime = time_client.getEpochTime();
    struct tm *ptm  = gmtime ((time_t *)&epochTime);
    current_day     = ptm->tm_mday;
    current_month   = ptm->tm_mon+1;
    current_year    = ptm->tm_year+1900;
    current_hours   = time_client.getHours();
    current_minutes = time_client.getMinutes();
    current_seconds = time_client.getSeconds();
    
  }

  // Sinon, on initialise l'horloge avec des valeurs par défaut
  else
  {
    current_day     = 1;
    current_month   = 1;
    current_year    = 1900;
    current_hours   = 0;
    current_minutes = 0;
    current_seconds = 0;
  }

  // Réglage du module RTC
  rtc.adjust(DateTime(current_year, current_month, current_day, current_hours, current_minutes, current_seconds));

  // Initialisation des boutons
  pinMode(hours_switch, INPUT);
  pinMode(minutes_switch, INPUT);

  brightnessLastUpdate = millis();

}

void loop()
{

  if (brightnessLastUpdate + brightnessUpdateDelay < millis())
  {
    
    brightnessLastUpdate = millis();
    
    // Récupération de la luminosité ambiante
    int lightValue = analogRead(A0);
    int brightnessValue = map(lightValue, 100, 1023, 0, 15);
    
    // Changement de la luminosité de l'affichage
    matrix.setBrightness(brightnessValue);
  
  }

  // Mise à jour de l'heure
  DateTime now = rtc.now();
  the_hours    = now.hour();
  the_minutes  = now.minute();

  // Réglage manuel de l'heure
  if (digitalRead(hours_switch) == HIGH)
    hours_offset++;
  
  if (digitalRead(minutes_switch) == HIGH)
    minutes_offset++;
  
  // Ajout de l'offset
  the_hours   += hours_offset;
  the_minutes += minutes_offset;

  the_minutes %= 60;
  the_hours   %= 24;

  unsigned digit3 = (the_minutes / 1U)  % 10;
  unsigned digit2 = (the_minutes / 10U) % 10;
  unsigned digit1 = (the_hours   / 1U)  % 10;
  unsigned digit0 = (the_hours   / 10U) % 10;

  bool willUpdate = false;

  if (digit3 != oldDigit3 || digit2 != oldDigit2 || digit1 != oldDigit1 || digit0 != oldDigit0)
  {
    willUpdate = true;
    oldDigit3 = digit3;
    oldDigit2 = digit2;
    oldDigit1 = digit1;
    oldDigit0 = digit0;
  }

  if (willUpdate)
  {

    // Si le chiffre des dizaines des heures est 0, on ne l'affiche pas
    // Cela permet d'afficher 0:30 et non 00:30
    digit0 == 0 ? matrix.writeDigitRaw(0, 0) : matrix.writeDigitNum(0, digit0);
    matrix.writeDigitNum(1, digit1);
    matrix.writeDigitNum(3, digit2);
    matrix.writeDigitNum(4, digit3);
    matrix.drawColon(true);
    matrix.writeDisplay();
    
  }
  
  delay(250);
  
}
