#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 

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
Encoder myEncoder(A0, A1);   // Initialize Rotary Encoder
bool backlightOn = true; // Variable to track the backlight status

// configure keypad (from LAB03)
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;
//const unsigned long animationDuration = 5000; // Duration of animation in milliseconds

bool menuDisplayed = false;             // Track if the menu is currently displayed or not
//bool animationRunning = true;           // Flag to control animation running
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);
    //pinMode(5,OUTPUT);


    Serial.begin(9600);
    lcd.init();
    lcd.backlight();
    startTime = millis(); // Record the start time
    //Liters per Minute
    attachInterrupt(0, increase, FALLING);
    //attachInterrupt(sensor, increase, FALLING);
}

void displayMenu()
{
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("A: Continuous");
    lcd.setCursor(0, 1);
    lcd.print("B: Flow Control");
    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
                //stopAnimation(); // Stop the animation
            }
        } else if (key == '#') { // If '#' key is pressed
            stopMode(); // Stop the current mode
        } else if (menuDisplayed) { // If menu is displayed
            if (key >= 'A' && key <= 'C' && 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 != ' ') { // If mode is selected
                // Handle mode-specific button presses here
            }
        }
    }
}

void stopMode() {
    // Stop the current mode
    if (flowDetectionRunning) {
        stopFlowDetection(); // Stop flow detection if running
    }
    // Reset mode-related variables and return to mode selection screen
    selectedMode = ' ';
    menuDisplayed = false;
    //animationRunning = true;
}

void stopFlowDetection() {
    // Stop the flow detection process
    // You may need to implement this function based on your requirements
    
    // Reset PWM to zero
    //analogWrite(motorDriver, 0);
    flowDetectionRunning = false; // Reset flow detection flag
}

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); // Display the actual accumulated volume
    lcd.print(" mL");
}

void modeC() {
    // Display Mode C menu with initial volume (0.0 mL)
    displayModeC(0.0);
    
    // Get the desired volume from the user
    float setVolume = getSetVolume();
    
    // Start flow detection
    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) {
            // Calculate flow rate every 'interval' milliseconds
            float flowRate = (pulse - totalPulses) * 60.0 / (interval * 7.5); // Assuming 7.5 pulses per liter
            totalPulses = pulse;
            lastTime = currentTime;

            // Update total volume
            totalVolume += flowRate / 1000.0; // Convert flow rate from L/min to L/s, then accumulate

            // Update LCD with actual accumulated volume under "Actual: " section
            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) {
                // If accumulated volume meets or exceeds set volume, stop the pump
                analogWrite(motorDriver, 0); // Stop the pump
                flowDetectionRunning = false; // Reset flow detection flag
                menuDisplayed = false; // Return to mode selection
                break;
            }
        }
    }
}

void increase()
{
    pulse++;    
}