#include <WiFi.h>
#include <PubSubClient.h>
#include "DHTesp.h"
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <ESP32Servo.h>
#include <ArduinoJson.h>

// Define pins for components
const int DHT_PIN = 15;
#define BUZZER 12
#define LDR1 34
#define LDR2 35
#define MOTOR 18

// Define variables for controlling the servo motor
float minAngle = 30.0;       // Minimum angle
float controllingFac = 0.75; // Controlling factor
float customAngle;
float customControlFac;

//Initialise clients and objects
Servo motor;
WiFiClient espClient;
PubSubClient mqttClient(espClient);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
JsonDocument packet;
DHTesp dhtSensor;

// Variables for schedule
bool isScheduledON = false; // Indicate if the schedule is enabled
unsigned long scheduledOnTime;

// Arrays to store data for MQTT messages
char tempAr[6];  
char motorAr[6];
char angleAr[6];
char ctrlAr[6];

// Constants for analog reading
const float analogMinValue = 0.0;
const float analogMaxValue = 1023.0;
const float intensityMin = 0.0;
const float intensityMax = 1.0;

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

    setupWifi();
    setupMqtt();

    dhtSensor.setup(DHT_PIN, DHTesp::DHT22);
    timeClient.begin();
    timeClient.setTimeOffset(5.5 * 3600);

    // Setup pins for components
    pinMode(BUZZER, OUTPUT);
    digitalWrite(BUZZER, LOW);
    pinMode(LDR1, INPUT);
    pinMode(LDR2, INPUT);
    motor.attach(MOTOR, 600, 2400);

    // Publish initial configuration to MQTT
    // connectToBroker();
    customAngle = 30;
    customControlFac = 0;
    mqttClient.publish("MQTT-SET-ANG", "30");
    mqttClient.publish("MQTT-SET-FAC", "0.75");
}

void loop()
{
    if (!mqttClient.connected())
    {
        connectToBroker();
    }

   // Maintain MQTT connection
    mqttClient.loop();

    updateTemperature();

    checkSchedule();

    updateLightIntensity(); // Read light intensity from LDR

    delay(1000);
}

// Setup MQTT client
void setupMqtt()
{
    mqttClient.setServer("test.mosquitto.org", 1883);
    mqttClient.setCallback(receiveCallback);
}

// Connect to MQTT broker
void connectToBroker()
{
    while (!mqttClient.connected())
    {
        Serial.println("Attempting MQTT connetion...");
        if (mqttClient.connect("ESP-55200255"))
        {
            Serial.println("Connected");
            mqttClient.subscribe("MQTT-ON-OFF");
            mqttClient.subscribe("MQTT-SCH-ON");
            mqttClient.subscribe("MQTT-DROP-DOWN");
            mqttClient.subscribe("MQTT-MIN-ANG");
            mqttClient.subscribe("MQTT-CTRL-FAC");
        }
        else
        {   
            Serial.print("Connection failed with state: ");
            Serial.println(mqttClient.state());
            delay(5000);
        }
    }
}

// MQTT receive callback function
void receiveCallback(char *topic, byte *payload, unsigned int length)
{
    Serial.print("Message arrived [");
    Serial.print(topic);
    Serial.print("] ");

    char payloadCharAr[length];

    for (int i = 0; i < length; i++)
    {
        Serial.print((char)payload[i]);
        payloadCharAr[i] = (char)payload[i];
    }

    Serial.println();

    // Process received MQTT messages
    if (strcmp(topic, "MQTT-ON-OFF") == 0)
    {
        buzzerOn(payloadCharAr[0] == '1');
    }
    else if (strcmp(topic, "MQTT-SCH-ON") == 0)
    {
        if (payloadCharAr[0] == 'N')
        {
            isScheduledON = false;
        }
        else
        {
            isScheduledON = true;
            scheduledOnTime = atol(payloadCharAr);
            Serial.println("Schedule ON");
            Serial.println("Scheduled Time is " + String(scheduledOnTime));
        }
    }
    // Update minimum angle
    if (strcmp(topic, "MQTT-MIN-ANG") == 0)
    {
        minAngle = atof(payloadCharAr);
        Serial.println(minAngle);
    }
    // Update controlling factor
    if (strcmp(topic, "MQTT-CTRL-FAC") == 0)
    {
        controllingFac = atof(payloadCharAr);
        Serial.println(controllingFac);
    }
    // Update minimum angle and controlling factor based on drop-down selection
    if (strcmp(topic, "MQTT-DROP-DOWN") == 0)
    {
        // Handle different drop-down options
        if (payloadCharAr[0] == 'D') //default
        {
            minAngle = 30;
            controllingFac = 0.75;
            Serial.println("Angle:" + String(minAngle) + " and Control Factor: " + String(controllingFac));
            mqttClient.publish("MQTT-SET-ANG", "30");
            mqttClient.publish("MQTT-SET-FAC", "0.75");
        }
        else if (payloadCharAr[0] == 'A')
        {
            minAngle = 30;
            controllingFac = 0.5;
            Serial.println("Angle:" + String(minAngle) + " and Control Factor: " + String(controllingFac));
            mqttClient.publish("MQTT-SET-ANG", "30");
            mqttClient.publish("MQTT-SET-FAC", "0.5");
        }
        else if (payloadCharAr[0] == 'B')
        {
            minAngle = 45;
            controllingFac = 0.3;
            Serial.println("Angle:" + String(minAngle) + " and Control Factor: " + String(controllingFac));
            mqttClient.publish("MQTT-SET-ANG", "45");
            mqttClient.publish("MQTT-SET-FAC", "0.3");
        }
        else if (payloadCharAr[0] == 'C')
        {
            minAngle = 60;
            controllingFac = 0.8;
            Serial.println("Angle:" + String(minAngle) + " and Control Factor: " + String(controllingFac));
            mqttClient.publish("MQTT-SET-ANG", "60");
            mqttClient.publish("MQTT-SET-FAC", "0.8");
        }
        else if (payloadCharAr[0] == 'X') //Custom
        { 
            String(customAngle, 2).toCharArray(angleAr, 6);
            String(customControlFac, 2).toCharArray(ctrlAr, 6);
            mqttClient.publish("MQTT-SET-ANG", angleAr);
            mqttClient.publish("MQTT-SET-FAC", ctrlAr);
            
            if (strcmp(topic, "MQTT-MIN-ANG") == 0)
            {
                minAngle = atof(payloadCharAr);
                customAngle = minAngle;
                //Serial.println("New custom angle is: " + String(minAngle));
                String(minAngle, 2).toCharArray(angleAr, 6);
                mqttClient.publish("MQTT-SET-ANG", angleAr);
            }
            if (strcmp(topic, "MQTT-CTRL-FAC") == 0)
            {
                controllingFac = atof(payloadCharAr);
                customControlFac = controllingFac;
                String(customControlFac, 2).toCharArray(ctrlAr, 6);
                //Serial.println("New custom factor is: " + String(controllingFac));
                mqttClient.publish("MQTT-SET-FAC", ctrlAr);
            }
        }
    }
}

// Function to turn the buzzer on or off
void buzzerOn(bool on)
{
    if (on)
    {
        tone(BUZZER, 256);
    }
    else
    {
        noTone(BUZZER);
    }
}

// Update temperature reading and publish to MQTT
void updateTemperature()
{
    TempAndHumidity data = dhtSensor.getTempAndHumidity();
    String(data.temperature, 2).toCharArray(tempAr, 6);

    //Serial.println("Temperature is " + String(tempAr) + "°C");
    mqttClient.publish("MQTT-TEMP", tempAr);
    delay((1000));
}

// Adjust the position of the shaded sliding window based on light intensity
void updateLightIntensity()
{
    float rightLDR = analogRead(LDR1);
    float leftLDR = analogRead(LDR2);
    float intensity = 0;
    char dataJson[50];

    if (rightLDR > leftLDR)
    {
        packet["LDR"] = "Right LED";
        if (rightLDR <= 1023)
        {
            intensity = (rightLDR - analogMinValue) / (analogMaxValue - analogMinValue);
           //Serial.print("Right LDR " + String(intensity) + " intensity");
            AdjustServoMotor(intensity, 0.5);
        }
        else
        {
            intensity = 1;
            //Serial.print("Right LDR " + String(intensity) + " intensity");
            AdjustServoMotor(intensity, 0.5);
        }
    }
    else
    {
        packet["LDR"] = "Left LED";
        if (leftLDR <= 1023)
        {
            intensity = (leftLDR - analogMinValue) / (analogMaxValue - analogMinValue);
            //Serial.print("Left LDR " + String(intensity) + " intensity");
            AdjustServoMotor(intensity, 1.5);
        }
        else
        {
            intensity = 1;
            //Serial.print("Left LDR " + String(intensity) + " intensity");
            AdjustServoMotor(intensity, 1.5);
        }
    }

    packet["Intensity"] = String(intensity);
    serializeJson(packet, dataJson);
    //Serial.println(dataJson);

    mqttClient.publish("MQTT-LIGHT-INTENSITY", dataJson);
}

// Setup WiFi connection
void setupWifi()
{
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println("Wokwi-GUEST");
    WiFi.begin("Wokwi-GUEST", "");

    while (WiFi.status() != WL_CONNECTED)
    {
        delay(500);
        Serial.print(".");
    }

    Serial.println("Wifi connected");
    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP());
}


// Get current time from NTP server
unsigned long getTime()
{
    timeClient.update();
    return timeClient.getEpochTime();
}

// Check if schedule time has arrived
void checkSchedule()
{
    if (isScheduledON)
    {
        unsigned long currentTime = getTime();
        if (currentTime > scheduledOnTime)
        {
            buzzerOn(true);
            mqttClient.publish("MQTT-SCH-OFF-ESP", "0");
            Serial.println("Current Time is " + String(currentTime));
            Serial.println("Scheduled Time is " + String(scheduledOnTime));
            isScheduledON = false;
        }
    }
}

// Adjust servo motor position based on light intensity and distance from minAngle
void AdjustServoMotor(double lightintensity, double D)
{
    double angle = minAngle * D + (180.0 - minAngle) * lightintensity * controllingFac; // Calculate the angle based on light intensity
    angle = min(angle, 180.0);
    // Serial.println(" and new angle: " + String(angle) + "°");
    motor.write(angle);
    String(angle-90, 2).toCharArray(motorAr, 6);
    mqttClient.publish("MQTT-MOTOR-ANG", motorAr);
}
$abcdeabcde151015202530fghijfghij