/*
  Coin acceptor with timer : 12-16-24
  unit: arduino mega
  auth: oliver feronel
  preliminary pin sets
*/
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
#define COIN_AC 2                     // Coin acceptor pin
#define SET_TIME_TYPE 4               // Set minutes or hour
#define UP_BTN 5                      // Move up time
#define DOWN_BTN 6                    // Move down time
#define SET_TIME 7                    // Set time if it's ok now
#define RELAY_1 8                     // SSR 1 connection
bool set_active = false;              // Check if set time is active -> allow edit
bool set_min = false;                 // Check if need to update minutes or hour
int hour = 0;
int minutes = 0;
unsigned long countdownDuration = 0; // Countdown duration in milliseconds
unsigned long previousMillis = 0;    // Stores the last time update was made
bool countdownActive = true;         // run countdown if thers still time else stop
unsigned long lastDebounceTime = 0;   // Last debounce time
unsigned long debounceDelay = 200;    // Debounce delay (milliseconds)
String lcd_line_0 = "";               // on this we store LCD data to be print so we can read it later
String lcd_line_1 = "";
String lcd_line_2 = "";
String lcd_line_3 = "";
void setup() {
  Serial.begin(115200);
  Serial.println("Start of everything here");
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd_line_0 = "WATER VENDO MACHINE";
  lcd.print(lcd_line_0);
  for (int i = 0; i < 10; i++) {
    byte value = EEPROM.read(i); // Read the value at address `i`
    // Check if the value is not in the expected range (e.g., ASCII range or custom validation)
    if (value < 0 || value > 127) { // Assuming valid data is within 0–127
      EEPROM.write(i, 0); // Reset the value to 0
      Serial.println("Writing to EEPROM address " + String(i) + " as Zero");
    } else {
      Serial.println("EEPROM address " + String(i) + " contains valid value: " + String(value));
    }
  }
  hour = EEPROM.read(0);
  minutes = EEPROM.read(5);
  Serial.println("Set Time is " + String(hour) + " in Hour " + String(minutes) + " in minutes");
  pinMode(COIN_AC, INPUT_PULLUP);
  pinMode(SET_TIME_TYPE, INPUT_PULLUP);
  pinMode(UP_BTN, INPUT_PULLUP);
  pinMode(DOWN_BTN, INPUT_PULLUP);
  pinMode(SET_TIME, INPUT_PULLUP);
  pinMode(RELAY_1, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(COIN_AC), coin_inserted_isr, FALLING); // interupt function for coin acceptor
  // delay(3000);
  // lcd.clear();
  // lcd.setCursor(0, );
  // lcd.print("TIME: " + formatTime(hour, minutes, 0));
  // lcd.setCursor(0, 1);
  // lcd.print("MAK TIMER");
}
void loop() {
   if(hour == 0 && minutes == 0){
    lcd_line_2 = "PLEASE SET TIME";
    lcd.setCursor(0, 2);
    lcd.print(lcd_line_2);
   }else if(lcd_line_2.indexOf("PLEASE SET TIME")){
    clearLine(2);
    lcd_line_2 = "";
   }
  if (debounce(SET_TIME)) {
    set_active = !set_active;
    if (set_active) {
      lcd.clear();
      lcd.print("Settings Active");
      delay(1500);
      lcd.clear();
      updateDisplay();
    } else {
      Serial.println("Settings Saved");
      lcd.clear();
      lcd.print("Settings Saved");
      delay(1500);
      
      lcd.setCursor(0, 0);
      lcd_line_0 = "WATER VENDO MACHINE";
      lcd.print(lcd_line_0);
      // updateDisplay();
    }
  }
  if (set_active) {
    handleSettings();
  } else {
    runCountdown();
  }
  
}
void handleSettings() {
  if (debounce(SET_TIME_TYPE)) {
    set_min = !set_min;
    clearLine(1);
    clearLine(2);
    lcd.setCursor(0, 1);
    lcd.print(set_min ? "Set: Minutes" : "Set: Hours");
    Serial.println(set_min ? "Setting Minutes" : "Setting Hours");
    delay(1000);
    updateDisplay();
  }
  if (debounce(UP_BTN)) {
    if (set_min) {
      minutes = (minutes + 1) % 60;
    } else {
      hour = (hour + 1) % 24;
    }
    updateDisplay();
  }
  if (debounce(DOWN_BTN)) {
    if (set_min) {
      minutes = (minutes > 0) ? (minutes - 1) : 59;
    } else {
      hour = (hour > 0) ? (hour - 1) : 23;
    }
    updateDisplay();
  }
}
void runCountdown() {
  if (!countdownActive) {
    lcd.setCursor(0, 1);
    lcd_line_1 = "PLEASE INSERT COIN";
    lcd.print(lcd_line_1 );
    digitalWrite(RELAY_1, LOW); // Turn off relay
    return; // No countdown to run
  }
  if (lcd_line_1.indexOf("PLEASE") != -1) {
    clearLine(1);
    lcd_line_1 = "";
  }
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= 1000) { // Update every second
    previousMillis = currentMillis;
    if (countdownDuration >= 1000) {
      countdownDuration -= 1000;
      int remainingHours = countdownDuration / 3600000UL;
      int remainingMinutes = (countdownDuration % 3600000UL) / 60000UL;
      int remainingSeconds = (countdownDuration % 60000UL) / 1000;
      lcd.setCursor(0, 1);
      lcd.print("TIME: " + formatTime(remainingHours, remainingMinutes, remainingSeconds));
      Serial.println("Remaining Time: " + formatTime(remainingHours, remainingMinutes, remainingSeconds));
      digitalWrite(RELAY_1, HIGH); // Turn on relay
    } else {
      countdownActive = false;
      digitalWrite(RELAY_1, LOW); // Turn off relay
      lcd.setCursor(0, 1);
      lcd_line_1 = "PLEASE INSERT COIN";
      lcd.print(lcd_line_1);
      Serial.println("Countdown Finished");
    }
  }
}
void coin_inserted_isr() {
  Serial.println("Coin inserted");
  unsigned long additionalTime = (hour * 3600000UL) + (minutes * 60000UL);
  countdownDuration += additionalTime;
  if (!countdownActive) {
    countdownActive = true;
    // digitalWrite(RELAY_1, HIGH); // Start relay
    previousMillis = millis();
  }
}
bool debounce(int pin) {
  bool reading = !digitalRead(pin);
  if (reading) {
    if (millis() - lastDebounceTime > debounceDelay) {
      lastDebounceTime = millis();
      return true;
    }
  }
  return false;
}
void updateDisplay() {
  lcd.setCursor(0, 0);
  lcd.print("TIME: " + formatTime(hour, minutes, 0));
  EEPROM.write(0, hour);
  EEPROM.write(5, minutes);
}
String formatTime(int h, int m, int s) {
  String timeStr = "";
  if (h < 10) timeStr += "0";
  timeStr += String(h) + ":";
  if (m < 10) timeStr += "0";
  timeStr += String(m) + ":";
  if (s < 10) timeStr += "0";
  timeStr += String(s);
  return timeStr;
}
void clearLine(int y) {
  // Clear LCD line start at 0
  String line_space = "";
  for (int i = 0; i < 20 ; i ++) { // Adjust '20' to match the width of your LCD
    line_space += " ";
  }
  lcd.setCursor(0, y); // Move cursor to the specified position
  lcd.print(line_space); // Print spaces to clear the line
  Serial.println("Clear line " + String(y)); // Log the action
}