#include <Wire.h>
#include <LiquidCrystal_I2C.h> //Liquid Crystal is LCD
#include "DHT.h" //For the humidifier

// Define I2C address
LiquidCrystal_I2C lcd(0x27, 16, 2); //Also sets up the size of the text

// Temperature and Humidity Sensor Setup
#define DHTPIN 4
#define DHTTYPE DHT11  // Change to your DHT sensor type if needed
DHT dht(DHTPIN, DHTTYPE);

// Ultrasonic Sensor Setup
const int trigPin = 2;
const int echoPin = 3;

//Various Integers
const int buttonTempHumPin = 9;
const int buttonDistancePin = 8;
const int pirPin = 5;            // PIR sensor connected to digital pin 5
const int buttonPIRPin = 10;      // Button for PIR sensor connected to digital pin 10
const int buttonFinalProjectPin = 11; // New button connected to pin 11

enum State {
  IDLE,
  SHOW_TEMP_HUM,
  MEASURE_DISTANCE,
  MEASURE_PIR,
  FINAL_PROJECT
};

State currentState = IDLE;
bool lastButtonTempHumState = HIGH;
bool lastButtonDistanceState = HIGH;
bool lastButtonPIRState = HIGH;
bool lastButtonFinalProjectState = HIGH;
unsigned long lastUpdateTimeDistance = 0; //Variable for both PIR and Distance Sensor
unsigned long lastUpdateTimePIR = 0; //

void setup() {
  // Initialize Temperature and Humidity Sensor
  dht.begin();

  // Initialize the LCD
  lcd.init(); 
  lcd.clear(); 
  lcd.backlight(); //Makes LCD light up from the back

  // Initialize Ultrasonic Sensor
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  // Initializing the Buttons
  pinMode(buttonTempHumPin, INPUT_PULLUP); //Used pullup because it didn't work without it
  pinMode(buttonDistancePin, INPUT_PULLUP);
  pinMode(buttonPIRPin, INPUT_PULLUP);
  pinMode(buttonFinalProjectPin, INPUT_PULLUP);

  lcd.setCursor(2, 0); //Manually sets the state to Idle
  lcd.print("Select an");
  lcd.setCursor(2, 1);
  lcd.print("Option");
  delay(2000);

  lcd.clear();

  // Allows the Serial Monitor
  Serial.begin(9600);
}

void loop() {
  // Read the button state for temperature and humidity
  bool currentButtonTempHumState = digitalRead(buttonTempHumPin);
  
  // Check for a button press (transition from HIGH to LOW)
  if (currentButtonTempHumState == LOW && lastButtonTempHumState == HIGH) {
    currentState = (currentState == SHOW_TEMP_HUM) ? IDLE : SHOW_TEMP_HUM;
    lcd.clear();
  }
  lastButtonTempHumState = currentButtonTempHumState;

  // Read the button state for distance measurement
  bool currentButtonDistanceState = digitalRead(buttonDistancePin);
  
  // Check for a button press (transition from HIGH to LOW)
  if (currentButtonDistanceState == LOW && lastButtonDistanceState == HIGH) {
    currentState = (currentState == MEASURE_DISTANCE) ? IDLE : MEASURE_DISTANCE;
    lcd.clear();
  }
  lastButtonDistanceState = currentButtonDistanceState;

  // Read the button state for the PIR sensor
  bool currentButtonPIRState = digitalRead(buttonPIRPin);

  // Check for a button press (transition from HIGH to LOW)
  if (currentButtonPIRState == LOW && lastButtonPIRState == HIGH) {
    currentState = (currentState == MEASURE_PIR) ? IDLE : MEASURE_PIR;
    lcd.clear();
  }
  lastButtonPIRState = currentButtonPIRState;

  // Read the state of the Final Project button
  bool currentButtonFinalProjectState = digitalRead(buttonFinalProjectPin);

  // Check for a button press (transition from HIGH to LOW)
  if (currentButtonFinalProjectState == LOW && lastButtonFinalProjectState == HIGH) {
    currentState = (currentState == FINAL_PROJECT) ? IDLE : FINAL_PROJECT;
    lcd.clear();
  }
  lastButtonFinalProjectState = currentButtonFinalProjectState;

  // The LCD states
  switch (currentState) {
    case IDLE:
      lcd.setCursor(2, 0);
      lcd.print("Select an");

      lcd.setCursor(2, 1);
      lcd.print("Option");
      break;

    case SHOW_TEMP_HUM:
      lcd.setCursor(0, 0);
      lcd.print("Humidity=");
      lcd.print((float)dht.readHumidity()); // print the humidity
      lcd.print("%");

      lcd.setCursor(0, 1);
      lcd.print("Temp=");
      lcd.print((float)dht.readTemperature()); // print the temperature
      lcd.print("C.");
      break;

    case MEASURE_DISTANCE:
      // Check if it's time to update the distance
      if (millis() - lastUpdateTimeDistance >= 1000) {
        long duration, distance;

        // Trigger ultrasonic sensor
        digitalWrite(trigPin, HIGH);
        delayMicroseconds(10);
        digitalWrite(trigPin, LOW);

        // Measure duration of echo
        duration = pulseIn(echoPin, HIGH);

        // Calculate distance in centimeters using the correct formula
        distance = duration * 0.034 / 2;

        // Clear LCD screen
        lcd.clear();

        // Display distance on LCD
        lcd.setCursor(3, 0);
        lcd.print(distance);

        lcd.setCursor(6, 0);
        lcd.print("cm");

        // Print distance to Serial Monitor
        Serial.print("Distance: ");
        Serial.print(distance);
        Serial.println(" cm");

        lastUpdateTimeDistance = millis();
      }
      break;

    case MEASURE_PIR:
      // Check if it's time to update the PIR sensor
      if (millis() - lastUpdateTimePIR >= 1000) {
        int motionSensorValue = digitalRead(pirPin);

        if (motionSensorValue == HIGH) {
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print("Motion Detected!");
          delay(2000); // Display message for 2 seconds
        } else {
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print("Waiting for");
          lcd.setCursor(0, 1);
          lcd.print("motion...");
        }

        lastUpdateTimePIR = millis();
      }
      break;

    case FINAL_PROJECT:
      // Display "Aarush Final Project"
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Aarush Final");
      lcd.setCursor(0, 1);
      lcd.print("Project");
      delay(2000);  // Display message for 2 seconds
            lcd.clear();

      // Reset the state to IDLE after displaying the message
      currentState = IDLE;
      break;
  }
}
