//Libreria per la gestione di NeoPixel
#include <Adafruit_NeoPixel.h>
#include <RTClib.h>
#include <SPI.h>
// Definizioni
//Pulsante On/Off
#define BTN_ON_OFF_PIN 7
//Pulsante per aumentare la luminosità
#define BTN_DIM_UP_PIN 3
//Pulsante per diminuire la luminosità
#define BTN_DIM_DOWN_PIN 4
//Pulsante per cambiare colore
#define BTN_COLOR_CHANGE_PIN 5
//Pulsante stato
#define BTN_STATE 6
//Pin Dati per NeoPixel
#define HOUR0_PIN 2
#define HOUR1_PIN 8
#define MIN0_PIN 9
#define MIN1_PIN 10
#define SEPARATOR_PIN 11
//Numero di LED della striscia
#define NUMPIXELS 28
#define SEPARATOR_PIXELS 2
//Fotoresistenza
#define PHOTORESISTOR_PIN A3
//Mappatura LED
#define ZERO (int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 24, 25, 26, 27}
#define ONE (int[]){4, 5, 6, 7, 8, 9, 10, 11}
#define TWO (int[]){0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15, 20, 21, 22, 23,24, 25, 26, 27}
#define THREE (int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 20, 21, 22, 23}
#define FOUR (int[]){4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 22, 23}
#define FIVE (int[]){0, 1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}
#define SIX (int[]){0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}
#define SEVEN (int[]){4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
#define EIGHT (int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}
#define NINE (int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}
//Istanzio oggetto NeoPixel
Adafruit_NeoPixel hour0 = Adafruit_NeoPixel(NUMPIXELS, HOUR0_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel hour1 = Adafruit_NeoPixel(NUMPIXELS, HOUR1_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel minute0 = Adafruit_NeoPixel(NUMPIXELS, MIN0_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel minute1 = Adafruit_NeoPixel(NUMPIXELS, MIN1_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel separator = Adafruit_NeoPixel(SEPARATOR_PIXELS, SEPARATOR_PIN, NEO_GRB + NEO_KHZ800);
//Definizione colori. I valori dei colori sono calcolati tenendo conton della luminosità
//La luminosità puà essere impostata su 5 valori, lo step fra l'uno e l'altro
//è pari a 51, partendo da 51 fino a 255, parto dalla metà che è pari a 153
//Valori validi per prima accensione dopo inseriento spina
#define WHITE hour0.Color(153, 153, 153) //Il colore rappresentato come unit32_t è 10066329
#define WARM_WHITE hour0.Color(153, 153, 138) //Il colore rappresentato come unit32_t è 10066314
#define RED hour0.Color(153, 0, 0) //Il colore rappresentato come unit32_t è 10027008
#define GREEN hour0.Color(0, 153, 0) //Il colore rappresentato come unit32_t è 39168
#define YELLOW hour0.Color(153, 153, 0) //Il colore rappresentato come unit32_t è 10066176
#define BLUE hour0.Color(0, 76, 153) //Il colore rappresentato come unit32_t è 19609
#define EMPTY hour0.Color(0, 0, 0)
// Stato del pulsante state
int state_ButtonState = 0;
// Ultimo stato del pulsante state
int state_LastButtonState = 0;
// State variable
static uint8_t state = 1;
//Variabile timer per pressione lunga
unsigned long t_pulsante = 0;
//Step di variazione della luminosità
unsigned int step = 51;
//Variabile di appoggio con il valore attuale dello step di luminosità
//per prima accensione dopo inserimento spina
unsigned int lum_corrente = 153;
//Variabile di buffer con il valore di luminosità attivo
//allo spegnimento della lampada
unsigned int buffer_luminosita = 0;
//Colore di partenza solo per prima accensione dopo inserimento spina
uint32_t colore = WHITE;
//Variabile di buffer con il colore attivo allo spegnimento della lampada
//Colori bufferColore = EMPTY;
uint32_t bufferColore = EMPTY;
//Ritardo per la funzione anti rimbalzo
int debounceDelay = 50;
//Variabile di verifica striscia On o Off
bool on = false;
//Variabili che contengono l'ora ed i minuti, vengono aggiornati dal modulo RTC
String ora = "00";
String minuti = "00";
//Create RTC_DS3231 instance
RTC_DS3231 rtc;
//Date & time variable
DateTime dateTime;
//Variables to store time characters to be printed
char strTime[7] = "    ";
String timeLine = String();
//Variabile controllo passaggio minuti
int lastMinute = -1;
//Variabile controllo passaggio secondi
int lastSecond = -1;
//Variabile booleana che indica se i separatori sono accesi o spenti
bool SwitchedOn = false;
void setup()
{
  hour0.begin();
  hour1.begin();
  minute0.begin();
  minute1.begin();
  separator.begin();
  Serial.begin(9600);
  //start I2C communication
  if (!rtc.begin()) {
    Serial.println("Impossibile trovare il modulo RTC");
    Serial.flush();
    while(1) delay(10);
  }
  //Da eliminare
  //rtc.adjust(DateTime(2025, 3, 23, 20, 30, 0));
  //getTime();
  //set initial Date-Time
  if (rtc.lostPower()) {
    Serial.println("Imposto l'ora....");
    // When time needs to be set on a new device, or after a power loss, the
    // following line sets the RTC to the date & time this sketch was compiled
    //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    rtc.adjust(DateTime(2025, 3, 23, 20, 50, 0));
    //rtc.adjust(strTime);
  }
  else Serial.println("Il modulo Rtc è in esecuzione....");
  pinMode(BTN_STATE, INPUT);
  Serial.println(luxmeter());
}
void loop()
{ 
  /*On_Off();
  getTime();
  
  Dim_up();
  
  Dim_down();
  
  Change_color();*/
  setState();
  
  switch (state)
  {
    case 1:
        getTime();
        Show();
      break;
    case 2:
        Dim_up();
        Dim_down();
        //getHour();
      break;
    case 3:
        Change_color();
        //getMinute();
      break;
    default:
      break;
  }
}
//========================== Funzioni =================================
//Funzione di gestione el tasto On/Off
void On_Off()
{
  //Se premo il pulsante On/Off e la striscia è spenta
  if (debounce(BTN_ON_OFF_PIN)==HIGH && !on) {
    //Memorizzo il valore millis
    t_pulsante = millis();
    //Verifico se il pulsante On/Off rimane premuto
    while(debounce(BTN_ON_OFF_PIN)==HIGH){
      //Se rimane premuto per il tempo prefissato
      if (millis() - t_pulsante >=1000) {
        //Controllo che il buffer colore non sia vuoto,
        //se non lo è, accendo i LED con quel valore
        if (bufferColore != EMPTY) {
			    ShowHour(ora, bufferColore);
          ShowMinute(minuti, bufferColore);
          //Assegno il flag di striscia accesa
          on = true;
        } else {
          //Assegno il colore tutti i LED della striscia
          bufferColore = colore;
          colore = bufferColore;
		      ShowHour(ora, bufferColore);
          ShowMinute(minuti, bufferColore);
          //Assegno il flag di striscia accesa
          on = true;
        }
      }
    }
  }
  //Se, invece, premo il pulsante On/Off e la striscia è accesa
  else if (debounce(BTN_ON_OFF_PIN)==HIGH && on) {
      //Salvo il valore di luminosità attivo
      buffer_luminosita = lum_corrente;
      //Salvo il colore attivo
      bufferColore = colore;
      //Spengo la striscia
      Clear_LED();
      //Assegno il flag di striscia spenta
      on = false;
  }
}
//Funzione che mostra l'orario
void Show() {
  //Controllo che il buffer colore non sia vuoto,
  //se non lo è, accendo i LED con quel valore
  if (colore != EMPTY) {
    ShowHour(ora, colore);
    ShowMinute(minuti, colore);
  } /*else {
    //Assegno il colore tutti i LED della striscia
    bufferColore = colore;
    colore = bufferColore;
    ShowHour(ora, bufferColore);
    ShowMinute(minuti, bufferColore);
  }*/
}
//Funzione di spegnimento della striscia
void Clear_LED() {
  //Assegno il colore tutti i LED della striscia
  for(int i = 0; i < NUMPIXELS; i++){
    hour0.setPixelColor(i, EMPTY);
    hour1.setPixelColor(i, EMPTY);
    minute0.setPixelColor(i, EMPTY);
    minute1.setPixelColor(i, EMPTY);
    //Spengo la striscia (assegnando colore 0,0,0 si spegne la striscia)
    hour0.show();
    hour1.show();
    minute0.show();
    minute1.show();
  }
}
void setState() {
  state_ButtonState = digitalRead(BTN_STATE);
  if(state_ButtonState != state_LastButtonState) {
    if(state_ButtonState == HIGH) {
      if(state == 4){
        state = 1;
      } else {
        state = state + 1;
      }
      Serial.println(state);
    }
    // Delay per evitare il bouncing
    delay(300);
  }
  state_LastButtonState = state_ButtonState;
}
//Funzione per aumentare la luminosità
void Dim_up() {
  //Alla pressione del pulsante per aumentare la luminosità
  if (debounce(BTN_DIM_UP_PIN)==HIGH) {
    //Controllo che il valore di una delle tre variabili non sia
    //già al massimo (le altre due seguono di pari passo)
    if (lum_corrente < 255) {
      //Salvo il valore di luminosità corrente
      lum_corrente = lum_corrente + step;
      switch (colore) {
        //Se il colore è WHITE
        case 10066329:
          //Assegno ai LED i nuovi valori
          ShowHour(ora, hour0.Color(lum_corrente, lum_corrente, lum_corrente));
          ShowMinute(minuti, hour0.Color(lum_corrente, lum_corrente, lum_corrente));
          break;
        //Se il colore è WARM_WHITE
        case 10066314:
          //Assegno ai LED i nuovi valori
          ShowHour(ora, hour0.Color(lum_corrente, lum_corrente, lum_corrente - 31));
          ShowMinute(minuti, hour0.Color(lum_corrente, lum_corrente, lum_corrente - 31));
          break;
        //Se il colore è RED
        case 10027008:
          //Assegno ai LED i nuovi valori
          ShowHour(ora, hour0.Color(lum_corrente, 0, 0));
          ShowMinute(minuti, hour0.Color(lum_corrente, 0, 0));
          break;
        //Se il colore è GREEN
        case 39168:
          //Assegno ai LED i nuovi valori
          ShowHour(ora, hour0.Color(0, lum_corrente, 0));
          ShowMinute(minuti, hour0.Color(0, lum_corrente, 0));
          break;
        //Se il colore è YELLOW
        case 10066176:
          //Assegno ai LED i nuovi valori
          ShowHour(ora, hour0.Color(lum_corrente, lum_corrente, 0));
          ShowMinute(minuti, hour0.Color(lum_corrente, lum_corrente, 0));
          break;
      }
    }
  }
}
//Funzione per diminuire la luminosità
void Dim_down() {
  //Alla pressione del pulsante per aumentare la luminosità
  if (debounce(BTN_DIM_DOWN_PIN)==HIGH) {
    //Controllo che il valore di una delle tre variabili non sia
    //già al massimo (le altre due seguono di pari passo)
    if (lum_corrente > 51) {
      //Salvo il valore di luminosità corrente
      lum_corrente = lum_corrente - step;
      switch (colore) {
        //Se il colore è WHITE
        case 10066329:
          //Assegno ai LED i nuovi valori
          ShowHour(ora, hour0.Color(lum_corrente, lum_corrente, lum_corrente));
          ShowMinute(minuti, hour0.Color(lum_corrente, lum_corrente, lum_corrente));
          break;
        //Se il colore è WARM_WHITE
        case 10066314:
          //Assegno ai LED i nuovi valori
          ShowHour(ora, hour0.Color(lum_corrente, lum_corrente, lum_corrente - 31));
          ShowMinute(minuti, hour0.Color(lum_corrente, lum_corrente, lum_corrente - 31));
          break;
        //Se il colore è RED
        case 10027008:
          //Assegno ai LED i nuovi valori
          ShowHour(ora, hour0.Color(lum_corrente, 0, 0));
          ShowMinute(minuti, hour0.Color(lum_corrente, 0, 0));
          break;
        //Se il colore è GREEN
        case 39168:
          //Assegno ai LED i nuovi valori
          ShowHour(ora, hour0.Color(0, lum_corrente, 0));
          ShowMinute(minuti, hour0.Color(0, lum_corrente, 0));
          break;
        //Se il colore è YELLOW
        case 10066176:
          //Assegno ai LED i nuovi valori
          ShowHour(ora, hour0.Color(lum_corrente, lum_corrente, 0));
          ShowMinute(minuti, hour0.Color(lum_corrente, lum_corrente, 0));
          break;
      }
    }
  }
}
//Funzione per il cambio colore
void Change_color() {
  //Alla pressione del pulsante per cambiare colore
  if (debounce(BTN_COLOR_CHANGE_PIN)==HIGH) {
    switch (colore) {
      //Se il colore è WHITE
      case 10066329:
      	colore = WARM_WHITE;
        ShowHour(ora, colore);
        ShowMinute(minuti, colore);
      	break;
      //Se il colore è WARM_WHITE
      case 10066314:
      	colore = RED;
        ShowHour(ora, colore);
        ShowMinute(minuti, colore);
      	break;
      //Se il colore è RED
      case 10027008:
      	colore = GREEN;
        ShowHour(ora, colore);
        ShowMinute(minuti, colore);
      	break;
      //Se il colore è GREEN
      case 39168:
      	colore = YELLOW;
        ShowHour(ora, colore);
        ShowMinute(minuti, colore);
      	break;
      //Se il colore è YELLOW
      case 10066176:
      	colore = BLUE;
        ShowHour(ora, colore);
        ShowMinute(minuti, colore);
      	break;
      //Se il colore è BLUE
      case 19609:
        colore = WHITE;
        ShowHour(ora, colore);
        ShowMinute(minuti, colore);
        break; 
    }
  }
}
//Funzione antirimbalzo
boolean debounce(int pin) {
  boolean state;
  boolean previousState;
  previousState = digitalRead(pin);
  for(int counter=0; counter < debounceDelay; counter++) {
    delay(1);
    state = digitalRead(pin);
    if( state != previousState) {
      counter = 0;
      previousState = state; }
  }
  return state;
}