#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// DHT Sensor setup
#define DHTPIN 15       
#define DHTTYPE DHT22   
DHT dht(DHTPIN, DHTTYPE);

// OLED setup
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// LED setup
#define LED_GREEN 12
#define LED_RED 13
#define LED_YELLOW 14

// WiFi credentials
const char* ssid = "Wokwi-GUEST";
const char* password = "";

// MQTT Server parameters
const char* mqtt_server = "broker.mqttdashboard.com";
const char* mqtt_topic = "wokwi-weather";

WiFiClient espClient;
PubSubClient client(espClient);

// Thresholds for temperature and humidity
float H_t = 40.0;
float L_t = 20.0; 
float H_h = 80.0; 
float L_h = 60.0;

String prevWeather = "";

void setup() {
  Serial.begin(115200);
  dht.begin();

  // OLED display initialization
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.print("Initializing...");
  display.display();

  // Initialize LED pins
  pinMode(LED_GREEN, OUTPUT);
  pinMode(LED_RED, OUTPUT);
  pinMode(LED_YELLOW, OUTPUT);

  // Turn off all LEDs initially
  digitalWrite(LED_GREEN, LOW);
  digitalWrite(LED_RED, LOW);
  digitalWrite(LED_YELLOW, LOW);

  // Connecting to WiFi
  Serial.print("Connecting to WiFi");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
  }
  Serial.println(" Connected!");

  // Connecting to MQTT server
  client.setServer(mqtt_server, 1883);
  Serial.print("Connecting to MQTT server... ");
  while (!client.connected()) {
    Serial.print(".");
    if (client.connect("ESP32Client")) {
      Serial.println(" Connected!");
    } else {
      delay(5000);
    }
  }

  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("WiFi & MQTT Connected!");
  display.display();
  delay(2000);
}

// Function to blink an LED
void blinkLED(int ledPin) {
  digitalWrite(ledPin, HIGH);
  delay(200);
  digitalWrite(ledPin, LOW);
  delay(200);
}

void controlLEDs(float temp, float hum) {
  if (temp >= L_t && temp <= H_t && hum >= L_h && hum <= H_h) {
    digitalWrite(LED_GREEN, HIGH);
    digitalWrite(LED_RED, LOW);
    digitalWrite(LED_YELLOW, LOW);
  } else {
    digitalWrite(LED_GREEN, LOW);

    if (temp < L_t) {
      digitalWrite(LED_RED, HIGH);
      digitalWrite(LED_YELLOW, LOW);
    } else if (temp > H_t) {
      blinkLED(LED_RED);
      digitalWrite(LED_YELLOW, LOW);
    }

    if (hum < L_h) {
      digitalWrite(LED_YELLOW, HIGH);
      digitalWrite(LED_RED, LOW);
    } else if (hum > H_h) {
      blinkLED(LED_YELLOW);
      digitalWrite(LED_RED, LOW);
    }
  }
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (client.connect("ESP32Client")) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  // Reading weather conditions
  float temperature = dht.readTemperature();
  float humidity = dht.readHumidity();

  if (isnan(temperature) || isnan(humidity)) {
    Serial.println("Failed to read from DHT sensor!");
    display.clearDisplay();
    display.setCursor(0, 0);
    display.print("DHT sensor error!");
    display.display();
    return;
  }

  String currentWeather = "{\"temp\": " + String(temperature) + ", \"humidity\": " + String(humidity) + "}";

  if (currentWeather != prevWeather) {
    // Display updated data on OLED
    display.clearDisplay();
    display.setCursor(0, 0);
    display.print("Temp: ");
    display.print(temperature);
    display.print(" C");
    display.setCursor(0, 16);
    display.print("Humidity: ");
    display.print(humidity);
    display.print(" %");
    display.display();

    // Control LEDs based on temperature and humidity
    controlLEDs(temperature, humidity);

    // Wait for the display to update before publishing to MQTT
    delay(1000);

    // Then publish data to MQTT
    Serial.println("Updated!");
    Serial.print("Reporting to MQTT topic ");
    Serial.print(mqtt_topic);
    Serial.print(": ");
    Serial.println(currentWeather);

    client.publish(mqtt_topic, currentWeather.c_str());
    prevWeather = currentWeather;
  } else {
    Serial.println("No change");
  }
  delay(2000);
}