#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <EEPROM.h>

const int passwordLength = 4; // Set the length of the password
char password[] = "1234";    // Set your desired password here
float pricePerLiter = 200;   // Default price per liter
const int eepromAddress = 0; // EEPROM address for storing the price

const int flowSensorPin = 2;    // Connect your flow sensor to this pin
const int solenoidValvePin = 3; // Connect your solenoid valve to this pin
const int waterPumpPin = 12;    // Connect your water pump to this pin
const int startButtonPin = 13;  // Connect a push button to this pin to start dispensing

volatile unsigned int pulseCount = 0;
float volumeDispensed = 0.0;
float amountSpent = 0.0;

short a = 0, i = 0, s = 0, j = 0; // Variables used later

float calibrationFactor = 90;

float flowRate = 0.0;
unsigned int flowMilliLitres = 0;
unsigned long totalMilliLitres = 0, volume = 0;

unsigned long oldTime;

const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {11, 10, 9, 8};
byte colPins[COLS] = {7, 6, 5, 4};

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
LiquidCrystal_I2C lcd(0x27, 16, 2);

bool dispensingInProgress = false; // Flag to indicate dispensing is in progress
bool dispenseButtonPressed = false; // Flag to indicate the dispense button is pressed

void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  pinMode(flowSensorPin, INPUT_PULLUP);
  pinMode(solenoidValvePin, OUTPUT);
  pinMode(waterPumpPin, OUTPUT);
  pinMode(startButtonPin, INPUT);

  attachInterrupt(digitalPinToInterrupt(flowSensorPin), pulseCounter, FALLING);

  // Read price per liter from EEPROM
  EEPROM.get(eepromAddress, pricePerLiter);

  updateLCD();
}

void loop() {
  char key = keypad.getKey();

  if (key != NO_KEY) {
    if (key == '*') {
      lcd.clear();
      lcd.print("Enter password");
      lcd.setCursor(0, 1);
      // User is entering the password
      char enteredPassword[passwordLength];
      int passwordIndex = 0;

      while (true) {
        key = keypad.getKey();

        if (key != NO_KEY) {
          if (key == 'A') {
            break; // Exit password entry if 'A' is pressed
          }
          enteredPassword[passwordIndex++] = key;
          //lcd.setCursor(0,1);
          lcd.print("*"); // Display asterisks to mask the password
        }

        if (passwordIndex == passwordLength) {
          // Check if the entered password is correct
          enteredPassword[passwordLength] = '\0'; // Null-terminate the entered password
          if (strcmp(enteredPassword, password) == 0) {
            // Password is correct, allow access to the menu
            inputMoney();
          } else {
            // Password is incorrect, show an error message
            lcd.clear();
            lcd.print("Invalid password");
            delay(1000);
            updateLCD();
          }
          break;
        }
      }
    }
    if (key == '#') {
      adjustPrice();
    }
    if (key == 'D') {
      // Clear the amount input
      lcd.clear();
      updateLCD();
    }
  }

  // Check if the start button is pressed to initiate dispensing
  //if (digitalRead(startButtonPin) == HIGH && !dispensingInProgress && dispenseButtonPressed) {
    // Call startDispensing with the calculated litersToDispense
      //startDispensing();
  //}
}

void inputMoney() {
  lcd.clear();
  lcd.print("EnterAmount:");
  Serial.println("Enter the amount to spend (e.g., 5.00): ");
  String inputAmount = "";

  while (true) {
    char key = keypad.getKey();
    if (key == 'A') {
      // Calculate the volume based on user input and price per liter
      float moneyToSpend = inputAmount.toFloat();
      float litersToDispense = moneyToSpend / pricePerLiter;

      // Prompt the user to press the dispense button
      lcd.clear();
      lcd.print("To despense");
      lcd.setCursor(0, 1);
      lcd.print (litersToDispense);
      lcd.setCursor(5,1);
      lcd.print("Liters");
      //lcd.print("Press DISPENSE");
      //lcd.setCursor(0, 1);
      //lcd.print("to start");
      dispenseButtonPressed = false; // Reset the dispense button flag

      

  while (true) {
        if (digitalRead(startButtonPin) == HIGH) {
          // Dispense button pressed, start dispensing
          startDispensing(litersToDispense);
          break; // Exit the inner loop
        }
      }
      break;
    } else if (key != NO_KEY) {
      inputAmount += key;
      lcd.print(key);
      Serial.print(key);
    }

    if (key == 'D') {
      // Clear the amount input
      lcd.clear();
      updateLCD();
    }
  }
}

void adjustPrice() {
  lcd.clear();
  lcd.print("Enter price:");
  Serial.println("Enter the new price per liter: ");
  String inputPrice = "";

  while (true) {
    char key = keypad.getKey();
    if (key == 'A') {
      break; // Exit loop when the '#' key is pressed
    } else if (key != NO_KEY) {
      inputPrice += key;
      lcd.print(key);
      Serial.print(key);
    }
  }

  pricePerLiter = inputPrice.toFloat();
  EEPROM.put(eepromAddress, pricePerLiter); // Store the new price in EEPROM

  lcd.clear();
  lcd.print("Price updated");
  delay(1000);
  updateLCD();
}

void startDispensing(float litersToDispense) {
  lcd.clear();
  flowRate = ((1000.0 / (millis() - oldTime)) * pulseCount) / calibrationFactor;
      oldTime = millis();
      flowMilliLitres = (flowRate / 60) * 1000;
      totalMilliLitres += flowMilliLitres;
 
      unsigned int frac;
      Serial.print("Flow rate :-");
      Serial.print(flowMilliLitres, DEC);
      Serial.print("mL/Second");
      Serial.print("\t");
      lcd.clear();
 
      lcd.setCursor(0, 0);
      lcd.print("Speed :");
      lcd.print(flowMilliLitres);
      lcd.print(" ml/s");
      Serial.print("Output Liquid Quantity: ");
      Serial.print(totalMilliLitres, DEC);
 
      Serial.println("mL");
      Serial.print("\t");
      lcd.setCursor(0, 1);
      lcd.print("Filled:");
      lcd.print(totalMilliLitres);
      lcd.setCursor(14, 1);
      lcd.print("ml");

  // Set dispensing flag to true
  dispensingInProgress = true;

  // Your dispensing logic goes here

  // Example: Dispense for a certain volume
  //float targetVolume = 1.0; // 1 liter, adjust as needed

  while (volumeDispensed < litersToDispense) {
    // Control the solenoid valve and water pump here
    digitalWrite(solenoidValvePin, HIGH); // Open solenoid valve
    digitalWrite(waterPumpPin, HIGH);     // Turn on water pump
    // You may need to adjust the flow rate control here based on your setup.
  }

  digitalWrite(solenoidValvePin, LOW); // Close solenoid valve
  digitalWrite(waterPumpPin, LOW);     // Turn off water pump

  // Reset dispensing flag
  dispensingInProgress = false;

  Serial.println("Dispensing complete");
  updateLCD();
}

void updateLCD() {
  lcd.clear();
  lcd.print("Press * = Unlock");
  lcd.setCursor(0, 1);
  lcd.print("Price/Li: ");
  lcd.setCursor(11, 1);
  lcd.print(pricePerLiter);
}

void pulseCounter() {
 pulseCount++;
  volumeDispensed = pulseCount / 7.5;
  updateLCD();
}