#include <LiquidCrystal_I2C.h>
#include "notemusicali.h"

// Creazione dell'oggetto per il display LCD
LiquidCrystal_I2C lcd(0x27, 20, 4);

#define BUZZER 8
#define LIVELLO_MASSIMO 100

// Buffer variabili pannello LCD
char buffer[10]; 

// Array di gioco
const byte pinLed[] = {9, 10, 11, 12}; // 
const byte pinBottoni[] = {2, 3, 4, 5};
const int noteMusicali[4] = {NOTE_G3, NOTE_C4, NOTE_E4, NOTE_G5};

// Tracking dei livelli
byte arrayLivelli[LIVELLO_MASSIMO];
byte indexLivello = 0;

// Aggiungi un livello alla sequenza (index++)
void nuovaSequenza() { 
  arrayLivelli[indexLivello] = random(0, 4);
  indexLivello++;
}

// Fai riprodurre la sequenza da ricordare.
void riproduciSequenza() {
  for(byte i = 0; i < indexLivello; i++) {
    digitalWrite(pinLed[arrayLivelli[i]], HIGH);
    tone(BUZZER, noteMusicali[arrayLivelli[i]]);
    coloreLCD(arrayLivelli[i]);
    delay(600);
    pulisciRigaLCD(1);
    digitalWrite(pinLed[arrayLivelli[i]], LOW);
    noTone(BUZZER);
    delay(100);
  }
}

// Leggi quale bottone ha premuto l'utente
byte leggiBottone() {
  while (1) {
    for (byte i = 0; i < 4; i++) {
      byte bottoneClick = pinBottoni[i];
      if (digitalRead(bottoneClick) == LOW) {
        coloreLCD(i);
        while (digitalRead(bottoneClick) == LOW) {
          // Attendi finché il bottone non viene rilasciato
          delay(10); 
        }
        return i;
      }
    }
  }
  return 0;
}

// Se l'utente ha sbagliato tasto
void haiPerso() {
  // Messaggio LCD che hai perso
  if(indexLivello - 1 >= 5) {
    lcd.clear();
    lcd.setCursor(1, 0);
    lcd.print("Bilardo, sei ancora");
    lcd.setCursor(3, 1);
    lcd.print("piu' bello ora!");
    lcd.setCursor(6, 3);
    sprintf(buffer, "Punti: %d", indexLivello - 1);
    lcd.print(buffer);
  }
  else {
    lcd.clear();
    lcd.setCursor(1, 0);
    lcd.print("Bilardo, ho delle");
    lcd.setCursor(1, 1);
    lcd.print("brutte notizie...");
    lcd.setCursor(6, 3);
    sprintf(buffer, "Punti: %d", indexLivello - 1);
    lcd.print(buffer);
  }

  // Melodia Wa-wa-waaa (si, sappiamo quale.)
  tone(BUZZER, NOTE_DS5);
  delay(300);
  tone(BUZZER, NOTE_D5);
  delay(300);
  tone(BUZZER, NOTE_CS5);
  delay(300);
  for (byte i = 0; i < 10; i++) {
    for (int i = -10; i <= 10; i++) {
      tone(BUZZER, NOTE_C5 + i);
      delay(5);
    }
  }
  noTone(BUZZER);
  delay(3000);
  lcd.clear();


  // Resetto l'index del livello per iniziare da capo
  indexLivello = 0;
}

// Controlla se l'utente ha premuto il bottone giusto
bool controllaSequenzaUtente() {

  // Ciclo per controllare se l'utente ha inserito un bottone sbagliato
  for(byte i = 0; i < indexLivello; i++) {
    byte bottoneGiusto = arrayLivelli[i];
    byte bottonePremuto = leggiBottone();    
    suonoLEDeAccensione(bottonePremuto);
    pulisciRigaLCD(1);
    if(bottonePremuto != bottoneGiusto) return false;
  }
  return true;
}


void setup() {
  // Inizializzo il pannello LCD
  lcd.init();
  lcd.backlight();

  // Inizializza i PIN delle periferiche
  for(byte i = 0; i < 4; i++) {
    pinMode(pinLed[i], OUTPUT);
    pinMode(pinBottoni[i], INPUT_PULLUP);
  }
  pinMode(BUZZER, OUTPUT);

  // Genera un seed per la generazione random dei numeri
  randomSeed(analogRead(A0));

  // Messaggio di benvenuto
  messaggioLCDiniziale();
}



void loop() {
  LCDpunteggio();
  nuovaSequenza();
  riproduciSequenza();
  delay(20);
  if(!controllaSequenzaUtente()) {
    haiPerso();
  }
  else {
    delay(250);
    suonoSequenzaCorretta();
    delay(250);
  }

}

// Accensione dei LED e suono (utente)
void suonoLEDeAccensione(byte LED) {

  digitalWrite(pinLed[LED], HIGH);
  tone(BUZZER, noteMusicali[LED]);
  delay(300);
  digitalWrite(pinLed[LED], LOW);
  noTone(BUZZER);
}
void suonoSequenzaCorretta() {
  tone(BUZZER, NOTE_C5);
  delay(200);
  tone(BUZZER, NOTE_E5);
  delay(200);
  tone(BUZZER, NOTE_G5);
  delay(200);

  tone(BUZZER, NOTE_E5);
  delay(200);
  noTone(BUZZER);
}
void messaggioLCDiniziale() {
  lcd.setCursor(5, 1);
  lcd.print("Benvenuto!");
  tone(BUZZER, NOTE_C4);
  delay(200);
  noTone(BUZZER);
  delay(500);
  lcd.clear();
  lcd.setCursor(5, 1);
  lcd.print("Fai almeno");
  lcd.setCursor(6, 2);
  lcd.print("5 PUNTI!");
  tone(BUZZER, NOTE_E4);
  delay(200);
  noTone(BUZZER);
  delay(500);
  lcd.clear();
  lcd.setCursor(1, 1);
  lcd.print("E il prof. sara'...");
  lcd.setCursor(7, 2);
  lcd.print("BELLO!");
  tone(BUZZER, NOTE_C4);
  delay(200);
  noTone(BUZZER);
  delay(500);
  lcd.clear();
  lcd.setCursor(1, 1);
  lcd.print("Scherzo, e' sempre");
  lcd.setCursor(7, 2);
  lcd.print("Bello!");
  tone(BUZZER, NOTE_E4);
  delay(200);
  noTone(BUZZER);
  delay(500);
  lcd.clear();
  lcd.setCursor(3, 1);
  lcd.print("BUONA FORTUNA!");
  tone(BUZZER, NOTE_C4);
  delay(200);
  noTone(BUZZER);
  delay(500);
  lcd.clear();
}
void coloreLCD(int index) {
  switch(index) {
    case 3:
      lcd.setCursor(7, 1);
      lcd.print("ROSSO");
      break;
    case 2:
      lcd.setCursor(8, 1);
      lcd.print("BLU");
      break;
    case 1:
      lcd.setCursor(7, 1);
      lcd.print("VERDE");
      break;
    case 0:
      lcd.setCursor(7, 1);
      lcd.print("GIALLO");
      break;
  }
}

void LCDpunteggio() {
    lcd.setCursor(6, 2);
    sprintf(buffer, "Punti: %d", indexLivello);
    lcd.print(buffer);
}

void pulisciRigaLCD(int riga) {
  lcd.setCursor(0, riga);
  for(int i = 0; i < 20; i++) {
    lcd.print(" ");
  }
}