#include <AccelStepper.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>
#include <OneButton.h> // Includi la libreria OneButton
// --- Definizione Pin e Oggetti ---
const int rs = 9, en = 10, d4 = 11, d5 = 12, d6 = 13, d7 = A0;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
const int stepPin = 4;
const int dirPin = 3;
AccelStepper stepper(AccelStepper::DRIVER, stepPin, dirPin);
const int btnOrario     = 5;
const int btnAntiorario = 6;
const int btnSalva      = 7;
const int btnVaiA       = 8;
// Oggetti OneButton per ogni pulsante
// OneButton(pin, activeLow, pullup)
OneButton btnOrario_obj(btnOrario, true, true);
OneButton btnAntiorario_obj(btnAntiorario, true, true);
OneButton btnSalva_obj(btnSalva, true, true);
OneButton btnVaiA_obj(btnVaiA, true, true);
// --- Costanti e Variabili di Sistema ---
const float stepPerMM = 100.0;
const int stepIncrement = 100;
const int maxPosizioni = 10;
long posizioni[maxPosizioni];
int posSalvate = 0;
int indiceCorrente = 0;      // Indice della *prossima* posizione nel ciclo "Vai"
int indiceVisualizzato = 0;  // Indice della posizione *attualmente* mostrata all'utente
long posizioneAttuale = 0;   // Posizione attuale del motore (in steps)
long offsetPosizione = 3000; // Offset iniziale o posizione di riferimento
const int eepromStart = 0; // Indirizzo di partenza in EEPROM per i dati
unsigned long lastUpdateDisplay; // Ultimo aggiornamento del display
// --- Prototipi delle Funzioni (Callback) ---
// Pulsanti Orario/Antiorario
void onOrarioHold();
void onAntiorarioHold();
void onOrarioReleased(); // Chiamata al rilascio di Orario
void onAntiorarioReleased(); // Chiamata al rilascio di Antiorario
// Pulsante Salva
void onSalvaClick(); // Potrebbe non servire, dato che usiamo il long press
void onSalvaLongPressStart(); // Inizio del long press
void onSalvaLongPressEnd();   // Fine del long press (rilascio dopo long press)
// Pulsante Vai
void onVaiClick();
// Funzioni di utilità
void updateDisplay();
void resetListaEEPROM();
// --- Setup ---
void setup() {
  Serial.begin(9600);
  lcd.begin(16, 4);
  lcd.print("Sistema pronto");
  delay(1000);
  stepper.setMaxSpeed(2500);
  stepper.setAcceleration(1000);
  // Non serve più pinMode per i pulsanti se usi OneButton con pullup=true
  // I pin vengono configurati dalla libreria
  // --- Collegamento dei callback ai pulsanti ---
  // Orario: movimento continuo se tenuto premuto, e rilascio
  btnOrario_obj.attachDuringLongPress(onOrarioHold); // Movimento continuo
  btnOrario_obj.attachRelease(onOrarioReleased);     // Al rilascio
  // Antiorario: movimento continuo se tenuto premuto, e rilascio
  btnAntiorario_obj.attachDuringLongPress(onAntiorarioHold); // Movimento continuo
  btnAntiorario_obj.attachRelease(onAntiorarioReleased);   // Al rilascio
  // Salva: Long press per salvare, e rilascio (per pulizia stato)
  btnSalva_obj.setPressTicks(1000); // Imposta il tempo per il long press (1 secondo)
  btnSalva_obj.attachLongPressStart(onSalvaLongPressStart); // Quando inizia il long press
  btnSalva_obj.attachLongPressStop(onSalvaLongPressEnd); // Quando il long press termina (rilascio)
  // Puoi anche usare attachClick se vuoi una breve pressione per altre azioni
  // btnSalva_obj.attachClick(onSalvaClick);
  // Vai: Click singolo per scorrere le posizioni
  btnVaiA_obj.attachClick(onVaiClick);
  // Se vuoi il reset con Salva+Vai, servirà una logica combinata nel loop o un approccio più avanzato
  // per OneButton che rileva due pulsanti contemporaneamente (non è integrato di default).
  // Per ora, manterremo la logica Salva+Vai nel loop per il reset.
  // --- Caricamento EEPROM e Inizializzazione Posizioni ---
  EEPROM.get(eepromStart, posSalvate);
  if (posSalvate < 0 || posSalvate > maxPosizioni) {
    posSalvate = 0;
  }
  for (int i = 0; i < posSalvate; i++) {
    EEPROM.get(eepromStart + sizeof(int) + (i * sizeof(long)), posizioni[i]);
  }
  if (posSalvate == 0) {
    posizioneAttuale = offsetPosizione;
    posizioni[0] = offsetPosizione;
  } else {
    posizioneAttuale = posizioni[0];
  }
  stepper.setCurrentPosition(posizioneAttuale);
  targetPosition = posizioneAttuale;
  indiceCorrente = 0;
  indiceVisualizzato = 0;
  lcd.clear();
  updateDisplay();
}
// --- Loop Principale ---
void loop() {
  // --- Aggiornamento dello stato dei pulsanti (Tick) ---
  btnOrario_obj.tick();
  btnAntiorario_obj.tick();
  btnSalva_obj.tick();
  btnVaiA_obj.tick();
  stepper.run();
  // --- Reset della lista (Salva + Vai premuti insieme) ---
  // Questa logica rimane qui perché OneButton non gestisce combinazioni dirette di pulsanti.
  // Puoi leggere lo stato RAW dei pin per questa combinazione.
  if (digitalRead(btnSalva) == LOW && digitalRead(btnVaiA) == LOW) {
      resetListaEEPROM();
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("EEPROM RESET!");
      delay(1000); // Bloccante, ma ok per un evento di reset raro
      updateDisplay();
      // Potrebbe essere necessario un piccolo delay per evitare doppi reset
      // o per impedire che i callback di OneButton vengano attivati subito dopo.
      // E.g., while(digitalRead(btnSalva) == LOW || digitalRead(btnVaiA) == LOW); // Attesa rilascio
      return;
  }
  // --- Aggiornamento Display per Movimento Manuale / A Riposo ---
  if (millis() - lastUpdateDisplay >= 500 && !stepper.isRunning()) {
    lastUpdateDisplay = millis();
    updateDisplay();
  }
  // Le azioni specifiche dei pulsanti sono ora gestite dai callback.
  // Il loop principale rimane più pulito.
}
// --- Funzioni di Callback ---
// --- Movimento Orario ---
void onOrarioHold() {
  targetPosition -= stepIncrement;
  indiceVisualizzato = -1; // Indica movimento manuale
  // updateDisplay() non qui per evitare flickering eccessivo
}
void onOrarioReleased() {
  // Azioni al rilascio del pulsante Orario (ad esempio, un suono, o reset di una modalità)
  // Puoi anche forzare un updateDisplay qui se vuoi l'aggiornamento finale appena rilasciato.
  // updateDisplay();
}
// --- Movimento Antiorario ---
void onAntiorarioHold() {
  targetPosition += stepIncrement;
  indiceVisualizzato = -1; // Indica movimento manuale
  // updateDisplay() non qui
}
void onAntiorarioReleased() {
  // updateDisplay();
}
// --- Pulsante Salva ---
void onSalvaLongPressStart() {
  // Quando il long press del Salva inizia
  Serial.println("Salva: Long Press START");
  // Prepara l'interfaccia utente per il salvataggio
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Salva...");
  lcd.setCursor(0, 1);
  lcd.print("Attendi 1 sec");
}
void onSalvaLongPressEnd() {
  // Quando il long press termina (pulsante rilasciato dopo 1 secondo)
  Serial.println("Salva: Long Press END (Salvataggio)");
  if (posSalvate < maxPosizioni) {
    posizioni[posSalvate] = stepper.currentPosition();
    EEPROM.put(eepromStart + sizeof(int) + (posSalvate * sizeof(long)), posizioni[posSalvate]); // Salva la singola posizione
    posSalvate++;
    EEPROM.put(eepromStart, posSalvate); // Aggiorna il conteggio
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Salvato piega:");
    lcd.print(posSalvate);
    lcd.setCursor(0, 1);
    lcd.print(stepper.currentPosition() / stepPerMM, 2);
    lcd.print(" mm");
    delay(500); // Breve pausa per visualizzare il messaggio
    indiceVisualizzato = posSalvate - 1; // Mostra l'ultima piega salvata
    indiceCorrente = 0;                 // Il prossimo "Vai" partirà dalla prima piega
  } else {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("LISTA PIENA!");
    delay(1000);
  }
  updateDisplay(); // Aggiorna il display allo stato finale
}
void onSalvaClick() {
  // Questa funzione verrebbe chiamata per un click breve (se l'hai attaccata)
  // Non fa nulla per ora, dato che il long press è per salvare.
  // Potrebbe essere usata per mostrare un messaggio "Tieni premuto per salvare".
  Serial.println("Salva: Click Breve");
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Tieni SALVA");
  lcd.setCursor(0, 1);
  lcd.print("per 1 sec");
  delay(1000); // Bloccante per un messaggio, ma ok per breve feedback
  updateDisplay();
}
// --- Pulsante Vai ---
void onVaiClick() {
  Serial.println("Vai: Click");
  if (posSalvate > 0 && !stepper.isRunning()) {
    if (indiceCorrente >= posSalvate) {
      indiceCorrente = 0;
    }
    // Aggiorna l'indice che verrà visualizzato con la posizione a cui stiamo andando
    indiceVisualizzato = indiceCorrente; 
    targetPosition = posizioni[indiceCorrente];
    stepper.moveTo(targetPosition);
    updateDisplay(); // Aggiorna il display SUBITO
    // Prepara indiceCorrente per la *prossima* pressione di "Vai"
    indiceCorrente = (indiceCorrente + 1) % posSalvate;
    // Nota: Il delay di 300ms che avevi prima qui non è più necessario né desiderabile
    // perché OneButton gestisce il debounce in modo asincrono.
    // Mettere un delay qui bloccherebbe il loop e renderebbe il sistema meno reattivo.
  } else if (posSalvate == 0) {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Lista vuota!");
      delay(1000);
      updateDisplay();
  }
}
// --- Funzioni di Utilità ---
void updateDisplay() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Piega N:");
  if (posSalvate == 0) {
    lcd.print("0 di 0");
  } else {
    if (indiceVisualizzato == -1) {
        lcd.print("Manuale");
    } else {
        lcd.print(indiceVisualizzato + 1);
        lcd.print(" di ");
        lcd.print(posSalvate);
    }
  }
  lcd.setCursor(0, 1);
  lcd.print("Pos: ");
  lcd.print(stepper.currentPosition() / stepPerMM, 2);
  lcd.print(" mm");
}
void resetListaEEPROM() {
  posSalvate = 0;
  indiceCorrente = 0;
  indiceVisualizzato = 0;
  for (int i = 0; i < maxPosizioni; i++) {
    posizioni[i] = 0;
  }
  EEPROM.put(eepromStart, posSalvate);
}