// #include <EEPROM.h>

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SoftwareSerial.h>

#define BUFFER_LEN 10      // number of data blocks (1 blk = 1 ulong)
#define BUFFER_START 0x10  // EEPROM address where buffer starts

#include "eewl_powerOffSafety.h"

unsigned long totalCycleCounter = 0;
EEWL pC(totalCycleCounter, BUFFER_LEN, BUFFER_START);

LiquidCrystal_I2C lcd(0x27, 20, 4);  // Create an Instance for LCD
// Bluetooth Serial Communication
SoftwareSerial BTSerial(10, 11);  // RX, TX

const byte dumpCycles = 10;  // Define number of allowed dump cycles

// Pin definitions
const byte sensorpin = 6;
const byte up_relay1pin = 8;     // Up Solenoid Relay 1
const byte down_relay2pin = 12;  // Down Solenoid Relay 2
const byte dump_relay3pin = 17;  // Dump Solenoid Relay 3

const byte buzzerpin = 9;

const byte button1 = 2;  // Mode selection Button
const byte button2 = 5;  // Raises the cylinder (activates the Up relay on Pin 8).
const byte button3 = 4;  // Lowers the cylinder (activates the Down relay on Pin 12).
const byte button4 = 3;  // Activates the Dump relay (Pin 17) and Down relay (Pin 12) for 1 second, then both go low.

const byte eepromAddress = 0;  // EEPROM address to start writing from, you can change this depending on your needs

bool prev4State = LOW;  // Variable to hold button 4 previous state

int cycleCounter = 0;  // Variable to hold cycle counter
const byte maxCycles = 20;
int selectedMenu = 0;

unsigned long debounceDelay = 250;
unsigned long debounce1Delay = 150;  // Debounce delay for button 1

// Variables to hold last button press time for dobounce delay
volatile unsigned long lastDebounceTime1 = 0;
unsigned long lastDebounceTime4 = 0;

enum MenuState {
  MENU_FAST_AUTO,
  MENU_HALF_DROP,
  MENU_MANUAL,
  MENU_UNDEFINED
};
enum SubState {
  SUB_STATE_1,
  SUB_STATE_2,
  NO_STATE
};

SubState currentSubState = SUB_STATE_1;

SubState lastSentSubState = NO_STATE;  // To track the last sent substate

MenuState currentMenuState = MENU_MANUAL;


// Variables for button states and initially set them LOW
volatile bool button1State = LOW;  // Variable to track button 1 state
bool button2State, button3State, button4State = LOW;
byte menuNumber = 1;  // This menu number will be send to Application

void button1Pressed() {
  if (((millis() - lastDebounceTime1) > debounce1Delay)) {  // Check if button 1 pressed
    button1State = HIGH;                                    // Set button 1 state as HIGH
    lastDebounceTime1 = millis();                           // Note current time when button is pressed
    Serial.println("Button pressed");
  }
}
void setup() {
  Serial.begin(9600);
  BTSerial.begin(9600);  // Initialize Bluetooth Serial
  // init EEPROM buffer
  pC.begin();

  // read into powerCycles the previous counter value or default value (0)
  if (!pC.get(totalCycleCounter))
    Serial.println("\nsetup: first time ever");

  lcd.init();
  lcd.setCursor(0, 0);
  lcd.backlight();
  lcd.display();
  // Set pin Modes of Button as iNPUT with PULL-UP
  pinMode(button1, INPUT_PULLUP);                                                 // Set button pin as input with Pull-UP
  attachInterrupt(digitalPinToInterrupt(button1), button1Pressed, FALLING);  // Attach interrup to button 1

  pinMode(button2, INPUT_PULLUP);  // Set button pin as input with Pull-UP
  pinMode(button3, INPUT_PULLUP);  // Set button pin as input with Pull-UP
  pinMode(button4, INPUT_PULLUP);  // Set button pin as input with Pull-UP

  pinMode(sensorpin, INPUT_PULLUP);

  // Set Pin modes of Solenoid as output
  pinMode(down_relay2pin, OUTPUT);
  pinMode(dump_relay3pin, OUTPUT);
  pinMode(up_relay1pin, OUTPUT);
  pinMode(buzzerpin, OUTPUT);
  resetCounters();

  displayMessage(0, 3, "Total:", HIGH);       // Print this message on dispaly and clear it as well
  displayMessage(8, 0, "Manual      ", LOW);  // Display current menu on screen adn dont' clear
  sendDataToPhone();                          // send Data to phone if connected
}
void resetCounters() {
  cycleCounter = 0;
  digitalWrite(down_relay2pin, LOW);
  digitalWrite(dump_relay3pin, LOW);
  digitalWrite(up_relay1pin, LOW);
}

void saveTotalCycleCounter() {
  pC.put(totalCycleCounter);
}


void loop() {
  unsigned long currentTime = millis();

  // Read physical button states and bluetooth Commands
  checkBluetoothCommands();  // Check for bluetooth
  readButtonStates();

  if (button1State == HIGH) {  // Check for button 1 press change menu state
    button1State = LOW;        // Reset button 1 state flag
    selectedMenu = (selectedMenu + 1) % 2;
    Serial.print(selectedMenu);
    // Menu state logic
    switch (selectedMenu) {
      case 0:
        currentMenuState = MENU_MANUAL;
        displayMessage(8, 0, "Manual      ", LOW);  // Display current menu on screen adn dont' clear (Add white space after name if we get raw data previously)
        menuNumber = 2;                             // Set menu number to 2 to be send to application
        sendDataToPhone();
        break;
      case 1:
        currentMenuState = MENU_FAST_AUTO;
        break;
        // ... [Add more cases for additional menus]
    }
    lastSentSubState = SUB_STATE_2;  // change last sub state
  }

  // Menu selection logic


  if (currentMenuState == MENU_FAST_AUTO && currentSubState == SUB_STATE_2 && button4State == HIGH && cycleCounter < dumpCycles) {
    // if (cycleCounter >= dumpCycles) {  // Check if cycle counter is greater then 10 reset it and change back to substate 1

    //   cycleCounter = 0;  // Reset the counter
    //   // currentSubState = SUB_STATE_1;  // Switch to Sub Menu 1
    //   // Optionally, reset other relevant states or counters as needed
    //   // This function resets cycleCounter and totalCycleCounter, adjust as necessary
    // }
    // ... [Add Fast Auto state functionality]            // Raise the cylinder if sensorpin is LOW and dump_relay3pin (dump) is LOW
    if (digitalRead(sensorpin) == HIGH) {  // if the sensor pin is not triggered, the Up relay (Pin 8) is activated
      digitalWrite(up_relay1pin, HIGH);    // Activate solenoid1 to raise
    } else {
      digitalWrite(up_relay1pin, LOW);  // Deactivate solenoid1
    }

    // If the sensor pin is triggered, the Dump relay (Pin 17) and Down relay (Pin 12) are activated for 1 second, and the system returns to Substate 1.
    if (digitalRead(sensorpin) == LOW) {
      digitalWrite(dump_relay3pin, HIGH);  // Activate solenoid2 dumping cylinder
      digitalWrite(down_relay2pin, HIGH);
      cycleCounter++;
      totalCycleCounter++;
      saveTotalCycleCounter();
      sendDataToPhone();  // Send updated data to phone
      customDelay(1000);
      sendDataToPhone();  // Send updated data to phone
      digitalWrite(dump_relay3pin, LOW);
      digitalWrite(down_relay2pin, LOW);  // Deactivate solenoid2
    }
  }
  // Menu functionality
  switch (currentMenuState) {
    case MENU_MANUAL:
      currentSubState = SUB_STATE_1;  // Change substate back to substate 1

      if (button2State == HIGH && digitalRead(sensorpin) == HIGH && digitalRead(dump_relay3pin) == LOW) {  // Button press raises cylinder
        digitalWrite(up_relay1pin, HIGH);
      } else if (button2State == LOW) {  // Button released Stop raising cylinder
        digitalWrite(up_relay1pin, LOW);
      }
      if (button3State == HIGH) {  // Lower cylinder with button press
        digitalWrite(down_relay2pin, HIGH);
      } else {
        digitalWrite(down_relay2pin, LOW);
      }
      if (button4State == HIGH) {            // button press dumps cylinder Activates the Dump relay (Pin 17) and Down relay (Pin 12) for 1 second
        digitalWrite(dump_relay3pin, HIGH);  // Activate Dump Relay
        digitalWrite(down_relay2pin, HIGH);  // Activate Down Relay
        // Mark and count cylinder dump
        cycleCounter++;  // Increase Cycle Count
        totalCycleCounter++;
        saveTotalCycleCounter();  // Save the counter to eeprom
        sendDataToPhone();        // Send updated data to phone
        customDelay(1000);
        sendDataToPhone();                  // Send updated data to phone
        button4State = LOW;                 // reset button 4 state
        digitalWrite(dump_relay3pin, LOW);  // Turn off  Dump relay
        digitalWrite(down_relay2pin, LOW);  // Turn off Down Relay
      }


      break;
    case MENU_FAST_AUTO:           // Switch-case logic for MENU_FAST_AUTO
      if (button4State == HIGH) {  // Toggle the substate on button 4 press
        if (currentSubState == SUB_STATE_1) {
          cycleCounter = 0;  // Reset the counter
          currentSubState = SUB_STATE_2;
          prev4State = HIGH;  // set button 4 previous state as high on substate 2
          menuNumber = 4;     // Set menu number to 4 to be send to application
          sendDataToPhone();
        } else if (prev4State == LOW) {  // Change state only when button 4 is released
          currentSubState = SUB_STATE_1;
          button4State = LOW;  // Reset button 4 state
        }
      }

      switch (currentSubState) {  // Check current sub state and take action
        case SUB_STATE_1:
          if (lastSentSubState != SUB_STATE_1) {        // If last substate is not substate 1 update it and turn All relay LOW
            displayMessage(8, 0, "AUTO STANDBY", LOW);  // Display current menu on screen adn dont' clear
            lastSentSubState = SUB_STATE_1;             // Set last substate as substate 1
            // Execute AUTO STANDBY specif
            digitalWrite(dump_relay3pin, LOW);  // Activate solenoid2 to dump
            digitalWrite(down_relay2pin, LOW);
            digitalWrite(up_relay1pin, LOW);
            menuNumber = 3;  // Set menu number to 3 to be send to application
            sendDataToPhone();
            // ... [Your specific logic for this sub-state]
          }
          break;

        case SUB_STATE_2:                             // Cases for substate 2
          lastSentSubState = SUB_STATE_2;             // change last sub state
          displayMessage(8, 0, "AUTO CYCLING", LOW);  // Display current menu on screen adn dont' clear

          break;  // Clear the LCD display at set intervals
      }
  }
}

void sendDataToPhone() {
  BTSerial.print(menuNumber);  // Get current menu number and update it on display
  BTSerial.print(";");
  BTSerial.print(totalCycleCounter);
  BTSerial.print(";");
  BTSerial.print(cycleCounter);
  BTSerial.println(";");  // Update the last sent menu state to MENU_IDLE
  // Update cycle and total cycle counters on LCD

  displayMessage(0, 2, cycleCounter);
  displayMessage(6, 3, totalCycleCounter);
}
void customDelay(unsigned long waitTime) {
  unsigned long cTime = millis();
  while ((millis() - cTime) < waitTime) {
    checkBluetoothCommands();  // Check for bluetooth commands
    readButtonStates();        // Read button states
  }
}
void readButtonStates() {

  if (digitalRead(button2) == LOW)        // Check if button 2 pressed
    button2State = HIGH;                  // Set button 2 state as HIGH
  else if (digitalRead(button2) == HIGH)  // Check if button 2 Released
    button2State = LOW;                   // Reset button 2 state back to low

  if (digitalRead(button3) == LOW)        // Check if button 3 pressed
    button3State = HIGH;                  // Set button 3 state as HIGH
  else if (digitalRead(button3) == HIGH)  // Check if button 3 Released
    button3State = LOW;                   // Reset Button 3 state back to low

  if ((digitalRead(button4) == LOW) && ((millis() - lastDebounceTime4) > debounceDelay)) {  // Check if button 1 pressed
    button4State = HIGH;                                                                    // Set button 4 state as HIGH
    lastDebounceTime4 = millis();                                                           // Note current time when button is pressed
  } else if (digitalRead(button4) == HIGH) {                                                // Check if button 4 is released and we are in substate 2
    if (currentSubState == SUB_STATE_2) {
      button4State = LOW;  // Reset button 4 state
      cycleCounter = 0;
      prev4State = LOW;
      currentSubState = SUB_STATE_1;  // Get back to substate 1
    }
  }
}
void checkBluetoothCommands() {
  while (BTSerial.available()) {
    char btChar = BTSerial.read();

    // Process single-character commands for Bluetooth control
    // Process button state commands
    switch (btChar) {
      case 'A': button1State = HIGH; break;
      case 'C': button2State = LOW; break;
      case 'D': button2State = HIGH; break;
      case 'E': button3State = LOW; break;
      case 'F': button3State = HIGH; break;
      case 'G': button4State = LOW; break;
      case 'H': button4State = HIGH; break;
    }
  }
}

void displayMessage(int x, int y, const String& msg, bool clearScreen) {
  // This function will receive cursor x, y position and message to display and a flag to clear previous display or not
  if (clearScreen) {  // If clear screen flag is high clear lcd
    lcd.clear();
    delay(500);  // Wait for 500 seconds
  }
  lcd.setCursor(x, y);  // Set cursor at x, y position
  lcd.print(msg);       // Print msg on lcd
}
void displayMessage(int x, int y, int value) {  // This function will receive cursor x, y position and integer value to display
  lcd.setCursor(x, y);                          // Set cursor at x, y position
  lcd.print(value);                             // Print msg on lcd
  lcd.print("  ");                              // Remove any extra value
}