#include <AccelStepper.h>
#include <EEPROM.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
const unsigned long interval = 75; // Intervallo per i movimenti manuali
const unsigned long velocitaMax = 2500; // massima velocita
const unsigned long accelerazione = 1000; // accelerazione
const unsigned int attesa = 4000; // Intervallo attesa piegatura
// --- Debounce Constants ---
const unsigned long debounceDelay = 50; // Tempo di debounce in ms
// --- Debounce Variables ---
int btnOrarioState;
int lastBtnOrarioState = HIGH;
unsigned long lastBtnOrarioChange = 0;
int btnAntiorarioState;
int lastBtnAntiorarioState = HIGH;
unsigned long lastBtnAntiorarioChange = 0;
int btnSalvaState;
int lastBtnSalvaState = HIGH;
unsigned long lastBtnSalvaChange = 0;
int btnVaiAState;
int lastBtnVaiAState = HIGH;
unsigned long lastBtnVaiAChange = 0;
// Variabili per lo stato debounced dei pulsanti
bool orarioPressedDebounced = false;
bool antiorarioPressedDebounced = false;
bool salvaPressedDebounced = false;
bool vaiPressedDebounced = false;
LiquidCrystal_I2C lcd(0x27, 20,4); // Indirizzo I2C standard (0x27 o 0x3F)
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;
const float stepPerMM = 100.0;
const int stepIncrement = 100; // Incremento/decremento per i movimenti manuali
const int maxPosizioni = 10;
long posizioni[maxPosizioni]; // Array per memorizzare le posizioni
int posSalvate = 0;           // Numero effettivo di posizioni salvate
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 lastMoveTime = 0;
long targetPosition = 0;      // Posizione target per lo stepper
unsigned long lastUpdateDisplay; // Ultimo aggiornamento del display
unsigned long salvaPressStart = 0;
bool salvaAttivo = false; // Flag per il long press del tasto Salva
void setup() {
    Serial.begin(9600);
    lcd.init();
    lcd.backlight();
    lcd.setCursor(0, 0);
    lcd.print("Sistema pronto");
    delay(1000);
    stepper.setMaxSpeed(velocitaMax);    // Velocità massima del motore
    stepper.setAcceleration(accelerazione); // Accelerazione del motore
    // Configurazione dei pin dei pulsanti con pull-up interni
    pinMode(btnOrario, INPUT_PULLUP);
    pinMode(btnAntiorario, INPUT_PULLUP);
    pinMode(btnSalva, INPUT_PULLUP);
    pinMode(btnVaiA, INPUT_PULLUP);
    // Carica il numero di posizioni salvate dalla EEPROM
    EEPROM.get(eepromStart, posSalvate);
    // Validazione: se il valore è fuori range, azzera
    if (posSalvate < 0 || posSalvate > maxPosizioni) {
        posSalvate = 0;
    }
    // Carica le posizioni effettive dalla EEPROM
    for (int i = 0; i < posSalvate; i++) {
        // Le posizioni sono salvate dopo il numero di posizioni (sizeof(int))
        EEPROM.get(eepromStart + sizeof(int) + (i * sizeof(long)), posizioni[i]);
    }
    // Inizializza la posizione attuale e il target
    if (posSalvate == 0) {
        posizioneAttuale = offsetPosizione;
        posizioni[0] = offsetPosizione; // Per visualizzazione iniziale
    } else {
        posizioneAttuale = posizioni[0];
    }
    stepper.setCurrentPosition(posizioneAttuale);
    targetPosition = posizioneAttuale;
    indiceCorrente = 0;      // La prossima piega da richiamare sarà la prima
    indiceVisualizzato = 0;  // Il display mostrerà la prima piega
    lcd.clear();
    updateDisplay(); // Aggiorna il display con lo stato iniziale
}
void loop() {
    stepper.run();
    checkButtons(); // Controlla e aggiorna lo stato debounced dei pulsanti
    static bool salvaPrev = false;
    static bool vaiPrev = false;
    static bool salvato = false;
    unsigned long currentMillis = millis();
    // --- Reset della lista se Salva + Vai sono premuti insieme ---
    if (salvaPressedDebounced && vaiPressedDebounced) {
        resetListaEEPROM();
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("EEPROM RESET!");
        delay(1000);
        updateDisplay();
        return;
    }
    // --- Movimento manuale con pulsanti Orario e Antiorario ---
    if (orarioPressedDebounced && currentMillis - lastMoveTime >= interval) {
        targetPosition -= stepIncrement;
        lastMoveTime = currentMillis;
        // Quando si muove manualmente, la posizione attuale è la posizione visualizzata
        indiceVisualizzato = -1; // Flag per indicare che non si è su una piega salvata
    }
    if (antiorarioPressedDebounced && currentMillis - lastMoveTime >= interval) {
        targetPosition += stepIncrement;
        lastMoveTime = currentMillis;
        indiceVisualizzato = -1; // Flag per indicare che non si è su una piega salvata
    }
    // Aggiornamento display quando lo stepper non è in movimento e dopo un certo tempo
    if (currentMillis - lastUpdateDisplay >= 500 && !stepper.isRunning()) {
        lastUpdateDisplay = currentMillis;
        posizioneAttuale = stepper.currentPosition();
        updateDisplay(); // Ricarica il display per mostrare la posizione attuale
    }
    stepper.moveTo(targetPosition);
    // --- Gestione del pulsante Salva (long press per salvare) ---
    if (salvaPressedDebounced) {
        if (!salvaPrev) {
            salvaPressStart = currentMillis;
            salvaAttivo = false;
            salvato = false;
        } else if (!salvaAttivo && (currentMillis - salvaPressStart >= 1000)) {
            salvaAttivo = true;
            if (posSalvate < maxPosizioni) {
                posizioni[posSalvate] = stepper.currentPosition();
                posSalvate++;
                EEPROM.put(eepromStart, posSalvate);
                for (int i = 0; i < posSalvate; i++) {
                    EEPROM.put(eepromStart + sizeof(int) + (i * sizeof(long)), posizioni[i]);
                }
                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);
                salvato = true;
                
                // Dopo il salvataggio, l'indice visualizzato è l'ultima piega salvata
                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);
            }
        } else if (!salvato) {
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("salva...");
            lcd.setCursor(0, 1);
            lcd.print((currentMillis - salvaPressStart) / 1000.0, 1);
            lcd.print(" sec");
        }
    }
    salvaPrev = salvaPressedDebounced;
    // --- Gestione del pulsante Vai (scorre le posizioni salvate) ---
    if (vaiPressedDebounced && !vaiPrev && posSalvate > 0 && !stepper.isRunning()) {
        // Assicurati che indiceCorrente sia valido prima di usarlo
        if (indiceCorrente >= posSalvate) {
            indiceCorrente = 0; // Torna alla prima posizione se l'indice è fuori range
        }
        //attesa
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("attesa.");
          lcd.print(attesa/1000);
          lcd.print("..sec..");  
        delay(attesa);
        // Aggiorna l'indice che verrà visualizzato con la posizione a cui stiamo andando
        indiceVisualizzato = indiceCorrente; 
        targetPosition = posizioni[indiceCorrente];
        stepper.moveTo(targetPosition);
        // Aggiorna il display SUBITO per mostrare la piega che sta per essere raggiunta
        updateDisplay(); 
        // Prepara indiceCorrente per la *prossima* pressione del pulsante "Vai"
        indiceCorrente = (indiceCorrente + 1) % posSalvate;
        delay(300);
    }
    vaiPrev = vaiPressedDebounced;
}
// --- Funzione per il Debounce dei pulsanti ---
void checkButtons() {
    unsigned long currentMillis = millis();
    // Debounce per btnOrario
    int readingOrario = digitalRead(btnOrario);
    if (readingOrario != lastBtnOrarioState) {
        lastBtnOrarioChange = currentMillis;
    }
    if (currentMillis - lastBtnOrarioChange > debounceDelay) {
        if (readingOrario != btnOrarioState) {
            btnOrarioState = readingOrario;
            if (btnOrarioState == LOW) {
                orarioPressedDebounced = true;
            } else {
                orarioPressedDebounced = false;
            }
        }
    }
    lastBtnOrarioState = readingOrario;
    // Debounce per btnAntiorario
    int readingAntiorario = digitalRead(btnAntiorario);
    if (readingAntiorario != lastBtnAntiorarioState) {
        lastBtnAntiorarioChange = currentMillis;
    }
    if (currentMillis - lastBtnAntiorarioChange > debounceDelay) {
        if (readingAntiorario != btnAntiorarioState) {
            btnAntiorarioState = readingAntiorario;
            if (btnAntiorarioState == LOW) {
                antiorarioPressedDebounced = true;
            } else {
                antiorarioPressedDebounced = false;
            }
        }
    }
    lastBtnAntiorarioState = readingAntiorario;
    // Debounce per btnSalva
    int readingSalva = digitalRead(btnSalva);
    if (readingSalva != lastBtnSalvaState) {
        lastBtnSalvaChange = currentMillis;
    }
    if (currentMillis - lastBtnSalvaChange > debounceDelay) {
        if (readingSalva != btnSalvaState) {
            btnSalvaState = readingSalva;
            salvaPressedDebounced = (btnSalvaState == LOW);
        }
    }
    lastBtnSalvaState = readingSalva;
    // Debounce per btnVaiA
    int readingVaiA = digitalRead(btnVaiA);
    if (readingVaiA != lastBtnVaiAState) {
        lastBtnVaiAChange = currentMillis;
    }
    if (currentMillis - lastBtnVaiAChange > debounceDelay) {
        if (readingVaiA != btnVaiAState) {
            btnVaiAState = readingVaiA;
            vaiPressedDebounced = (btnVaiAState == LOW);
        }
    }
    lastBtnVaiAState = readingVaiA;
}
// --- Funzioni Ausiliarie ---
void updateDisplay() {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Piega N:");
    if (posSalvate == 0) {
        lcd.print("0 di 0");
    } else {
        // Se indiceVisualizzato è -1 (movimento manuale o non su una piega salvata), non mostrare indice
        if (indiceVisualizzato == -1) {
            lcd.print("<---->"); // O un altro messaggio che indichi che non è una piega numerata
        } else {
            lcd.print(indiceVisualizzato + 1); // Usa indiceVisualizzato per la stampa
            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; // Reset anche qui
    for (int i = 0; i < maxPosizioni; i++) {
        posizioni[i] = 0;
    }
    EEPROM.put(eepromStart, posSalvate);
}