#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <Encoder.h> // Include the Rotary Encoder library

LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); // Initialize 16x2 LCD

#define buzzer 3
#define motorDriver 4
#define sensor 2 

byte sensorInt = 0; 
volatile long pulse;
unsigned long lastTime;
float volume;
float desiredFlowRate = 4.5; // Default desired flow rate
float desiredVolume = 0.0;   // Variable to store the desired volume set by the user

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'}};

const byte rowPins[ROWS] = {13, 12, 11, 10};
const byte colPins[COLS] = {9, 8, 7, 6};

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

unsigned long startTime;
bool menuDisplayed = false;             // Track if the menu is currently displayed or not
char selectedMode = ' ';                // Default selected mode is none
bool flowDetectionRunning = false;      // Flag to indicate if flow detection is running

void setup()
{
    pinMode(buzzer, OUTPUT);
    pinMode(sensor, INPUT);
    pinMode(motorDriver, OUTPUT);
    
    lcd.init();
    lcd.backlight();
    startTime = millis(); // Record the start time
    //Liters per Minute
    pulse=0;
    attachInterrupt(sensorInt, increase, FALLING);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Kontrol Aliran");   
    lcd.setCursor(0, 1);
    lcd.print("Tekan * ");
}

void displayMenu()
{
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Tekan B ");
    lcd.setCursor(0, 1);
    lcd.print("Untuk Melanjutkan");
    menuDisplayed = true;
}

void loop() {
    char key = keypad.getKey(); // Get the key pressed
    if (key != NO_KEY) {
        tone(buzzer, 1300, 50); // Play tone for keypress

        if (key == '*') { // If asterisk (*) key is pressed
            if (!menuDisplayed) { // If menu is not already displayed
                displayMenu(); // Display the menu                
            }
        } else if (key == '#') { // If '#' key is pressed            
        } else if (menuDisplayed) { // If menu is displayed
            if (key >= 'A' && key <= 'B' && selectedMode == ' ') { // If a valid mode key is pressed and no mode is currently running
                selectedMode = key;
                switch (selectedMode) {
                     case 'B':
                        modeC(); // Enter Mode C
                        break;
                }
            } else if (selectedMode != ' ') {
            }
        }
    }
}

void displayModeC(float actualVolume)
{
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Set Point:");
    lcd.setCursor(0, 1);
    lcd.print("Actual: ");
    lcd.print(actualVolume, 1); 
    lcd.print(" mL");
}

void modeC() 
{
  displayModeC(0.0);
  float setVolume = getSetVolume();    
  if (flowDetectionRunning) 
  {
    detectFlow(setVolume);
  }
}

float getSetVolume() {
    // Get the desired volume from the user using the keypad
    // This function should wait until the user presses '*' to confirm the input
    float volume = 0.0;
    bool volumeEntered = false;
    while (!volumeEntered) {
        char key = keypad.getKey();
        if (key >= '0' && key <= '9') {
            volume = volume * 10 + (key - '0');
            lcd.setCursor(10, 0);
            lcd.print(volume);
        } else if (key == '*') {
            volumeEntered = true;
            flowDetectionRunning = true; // Set flow detection flag
        }
    }
    return volume;
}

void detectFlow(float setVolume) {
    unsigned long lastTime = 0;
    unsigned long interval = 1000; // Interval for flow rate calculation (in milliseconds)
    unsigned long totalPulses = 0;
    float totalVolume = 0.0;

    lcd.setCursor(8, 1);
    lcd.print("       "); // Clear previous value

    while (true) {
        unsigned long currentTime = millis();
        if (currentTime - lastTime >= interval) 
        {
            detachInterrupt(sensorInt);
            float flowRate = (pulse - totalPulses) * 60.0 / (interval * 7.5); // Assuming 7.5 pulses per liter
            totalPulses = pulse;
            lastTime = currentTime;
            totalVolume += flowRate / 1000.0; // Convert flow rate from L/min to L/s, then accumulate
            lcd.setCursor(8, 1);
            lcd.print(totalVolume, 1); // Print actual accumulated volume with 1 decimal place
            lcd.print(" mL");
            // Check if accumulated volume meets or exceeds set volume
            if (totalVolume >= setVolume) { //Jika volume yang di terima melebihi volume set point maka pompa berhenti
              analogWrite(motorDriver, 0); // Stop the pump
              flowDetectionRunning = false; // Reset flow detection flag
              menuDisplayed = false; // Return to mode selection
              break;
            }
            pulse=0;
            attachInterrupt(sensorInt, increase, FALLING);
        }
    
    }
}

void increase()
{
    pulse++;        
}