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

#define DHT_PIN 15
#define BUZZER 12
#define LEFT_LDR_PIN A0
#define RIGHT_LDR_PIN A1
#define MAIN_SWITCH_TOPIC "ENTC-ADMIN-MAIN-SWITCH"

WiFiClient espClient;
PubSubClient mqttClient(espClient);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
DHTesp dhtSensor;
Servo servoMotor;

char tempAr[6];
bool isScheduledON = false;
unsigned long scheduledOnTime;
bool mainSwitchOn = true;
const int thetaOffset = 30;
const float gamma = 0.75;

void setup() {
  Serial.begin(115200);
  setupWiFi();
  setupMqtt();
  dhtSensor.setup(DHT_PIN, DHTesp::DHT22);
  timeClient.begin();
  timeClient.setTimeOffset(5.5 * 3600);  
  pinMode(BUZZER, OUTPUT);
  pinMode(LEFT_LDR_PIN, INPUT);
  pinMode(RIGHT_LDR_PIN, INPUT);
  servoMotor.attach(13); // Pin for servo motor
  digitalWrite(BUZZER, LOW);
}

void loop() {
  if(!mqttClient.connected()) {
    connectToBroker();
  }
  mqttClient.loop();
  updateTemperature();
  float leftLDRIntensity = getLDRIntensity(LEFT_LDR_PIN);
  float rightLDRIntensity = getLDRIntensity(RIGHT_LDR_PIN);
  float maxIntensity = max(leftLDRIntensity, rightLDRIntensity);
  float motorAngle = calculateMotorAngle(maxIntensity);
  servoMotor.write(motorAngle);
  mqttClient.publish("ENTC-ADMIN-TEMP", tempAr);
  checkSchedule();
  delay(1000);
}

void buzzerOn(bool on) {
  if (on) {
    tone(BUZZER, 256);
  } else {
    noTone(BUZZER);
  }
}

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

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();
  if (strcmp(topic, "ENTC-ADMIN-MAIN-ON-OFF") == 0) {
    buzzerOn(payloadCharAr[0] == '1');
  } else if(strcmp(topic, "ENTC-ADMIN-SCH-ON") == 0) {
    if(payloadCharAr[0] == 'N') {
      isScheduledON = false;
    } else {
      isScheduledON = true;
      scheduledOnTime = atol(payloadCharAr);
    }
  } else if(strcmp(topic, MAIN_SWITCH_TOPIC) == 0) {
    mainSwitchOn = (payloadCharAr[0] == '1');
    if (!mainSwitchOn) {
      buzzerOn(false);
    }
  }
}

void connectToBroker() {
  while (!mqttClient.connected()) {
    Serial.print("Attempting MQTT connection...");
    if(mqttClient.connect("ESP32-12345645454")) {
      Serial.println("connected ");
      mqttClient.subscribe("ENTC-ADMIN-MAIN-ON-OFF");
      mqttClient.subscribe("ENTC-ADMIN-SCH-ON");
      mqttClient.subscribe(MAIN_SWITCH_TOPIC);
    } else {
      Serial.print("failed ");
      Serial.print(mqttClient.state());
      delay(5000);
    }
  }
}

void updateTemperature() {
  TempAndHumidity data = dhtSensor.getTempAndHumidity();
  String(data.temperature, 2).toCharArray(tempAr, 6);
}

void setupWiFi() {
  Serial.println();
  Serial.print("Connecting to WiFi");
  WiFi.begin("Wokwi-GUEST", "", 6);
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
  }
  Serial.println(" Connected!");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

unsigned long getTime() {
  timeClient.update();
  return timeClient.getEpochTime();
}

void checkSchedule() {
  if(isScheduledON) {
    unsigned long currentTime = getTime();
    if (currentTime > scheduledOnTime) {
      buzzerOn(true);
      isScheduledON = false;
      mqttClient.publish("ENTC-ADMIN-MAIN-ON-OFF-ESP", "1");
      mqttClient.publish("ENTC-ADMIN-SCH-ESP-ON", "0");
      Serial.println("Scheduled ON");
    }
  }
}

float getLDRIntensity(int pin) {
  int rawValue = analogRead(pin);
  return map(rawValue, 0, 1023, 0, 1); // Mapping to range 0-1
}

float calculateMotorAngle(float intensity) {
  float D = (intensity == getLDRIntensity(LEFT_LDR_PIN)) ? 1.5 : 0.5;
  float motorAngle = min(thetaOffset * D + (180 - thetaOffset) * intensity * gamma, 180);
  return motorAngle;
}