#define BLYNK_PRINT Serial
#define BLYNK_TEMPLATE_ID "TMPxxxxxx"
#define BLYNK_TEMPLATE_NAME "Device"
#define BLYNK_AUTH_TOKEN "zhdZE_oN6NHiQfBx54oPG81E9Bns9WUC"
#define TRIGGER_PIN 23 // Pin trigger HC-SR04
#define ECHO_PIN 22 // Pin echo HC-SR04
#define MAX_DISTANCE 400 // Jarak maksimum yang dapat diukur, maks 400 cm

#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <BlynkSimpleEsp32.h>
#include <NewPing.h> // Library sensor PING 
#include <ESP32Servo.h>

char ssid[] = "Wokwi-GUEST";
char pass[] = "";
bool ledStatus1 = false;
bool ledStatus2 = false;
bool ledStatus3 = false;
bool autostatus = false;
const int servoPin = 25;
const int buttonPin = 19;
const int led_statuspin = 2;
char auth[] = "zhdZE_oN6NHiQfBx54oPG81E9Bns9WUC";
const char servername[] = "iot.serangkota.go.id";  // kalau gak bisa pakai IPAddress(103,102,250,127)
const uint16_t port = 8080;
unsigned long before = 0;
unsigned long interval = 1000;
unsigned long resetbefore = 1000;
unsigned long resetinterval = 1000;

#define BLYNK_GREEN "#23C48E"
#define BLYNK_BLUE "#04C0F8"
#define BLYNK_YELLOW "#ED9D00"
#define BLYNK_RED "#D3435C"
#define BLYNK_DARK_BLUE "#5F7CD8"

WidgetLED led_malam(V9);
WidgetLED led_sore(V10);
WidgetLED led_pagi(V11);
WidgetLED led_stat(V14);

BlynkTimer timer;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 25200);

int NowJam = 0;
int NowMinute = 0;
int pagi_hours, pagi_minutes;
int sore_hours, sore_minutes;
int malam_hours, malam_minutes;
bool pakan_pagi = false;
bool pakan_sore = false;
bool pakan_malam = false;

unsigned long ledbefore = 0;


NewPing sonar (TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
Servo myServo;

void setup() {
  // Debug console
  Serial.begin(115200);
  Serial.print("Connecting to WiFi");
  myServo.attach(servoPin);
  WiFi.begin("Wokwi-GUEST", "");
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
  }
  pinMode(buttonPin, INPUT);
  pinMode(led_statuspin, OUTPUT);
  digitalWrite(led_statuspin, LOW);
  Serial.println(" Connected!");
  Serial.println(" Blynk Connecting!");
  timeClient.begin();
  timeClient.update();
  Blynk.begin(auth, "Wokwi-GUEST", "", servername, port);

  led_pagi.off();   // Matikan lampu pagi
  led_sore.off();   // Matikan lampu sore
  led_malam.off();  // Matikan lampu sore
  led_stat.off();
}

BLYNK_CONNECTED() {
  Blynk.syncVirtual(V3, V4, V5);
}

BLYNK_WRITE(V1) {
  int pinValue = param.asInt();  // Membaca nilai dari widget Blynk
  if (pinValue == 1) {
    myServo.write(90);
    delay(1000); // Tunggu 1 detik
    myServo.write(0);
  } else if (pinValue == 0) {
    myServo.write(0);
  }
}

BLYNK_WRITE(V3) {
  long startTimeInSecs = param[0].asLong();     // Menerima waktu dari Blynk dalam detik
  int hours = startTimeInSecs / 3600;           // Hitung jumlah jam
  int minutes = (startTimeInSecs % 3600) / 60;  // Hitung jumlah menit
  int seconds = startTimeInSecs % 60;           // Hitung jumlah detik
  malam_hours = hours;
  malam_minutes = minutes;
  // Format waktu dalam string HH:MM
  String formattedTime = "";
  if (hours < 10) {
    formattedTime += "0";  // Tambahkan "0" di depan jika jam kurang dari 10
  }
  formattedTime += String(hours) + ":";  // Tambahkan jam ke dalam string
  if (minutes < 10) {
    formattedTime += "0";  // Tambahkan "0" di depan jika menit kurang dari 10
  }
  formattedTime += String(minutes);  // Tambahkan menit ke dalam string

  Blynk.virtualWrite(V6, formattedTime);

  // Tampilkan waktu di Serial Monitor (opsional)
  Serial.print("Jadwal Pakan Malam Ditetapkan : ");
  Serial.println(formattedTime);
}

BLYNK_WRITE(V4) {
  long startTimeInSecs = param[0].asLong();     // Menerima waktu dari Blynk dalam detik
  int hours = startTimeInSecs / 3600;           // Hitung jumlah jam
  int minutes = (startTimeInSecs % 3600) / 60;  // Hitung jumlah menit
  int seconds = startTimeInSecs % 60;           // Hitung jumlah detik
  sore_hours = hours;
  sore_minutes = minutes;
  // Format waktu dalam string HH:MM
  String formattedTime = "";
  if (hours < 10) {
    formattedTime += "0";  // Tambahkan "0" di depan jika jam kurang dari 10
  }
  formattedTime += String(hours) + ":";  // Tambahkan jam ke dalam string
  if (minutes < 10) {
    formattedTime += "0";  // Tambahkan "0" di depan jika menit kurang dari 10
  }
  formattedTime += String(minutes);  // Tambahkan menit ke dalam string

  // Kirim waktu ke widget V5 di Blynk
  Blynk.virtualWrite(V7, formattedTime);

  // Tampilkan waktu di Serial Monitor (opsional)
  Serial.print("Jadwal Pakan Sore Ditetapkan : ");
  Serial.println(formattedTime);
}

BLYNK_WRITE(V5) {
  long startTimeInSecs = param[0].asLong();     // Menerima waktu dari Blynk dalam detik
  int hours = startTimeInSecs / 3600;           // Hitung jumlah jam
  int minutes = (startTimeInSecs % 3600) / 60;  // Hitung jumlah menit
  int seconds = startTimeInSecs % 60;           // Hitung jumlah detik
  pagi_hours = hours;
  pagi_minutes = minutes;
  // Format waktu dalam string HH:MM
  String formattedTime = "";
  if (hours < 10) {
    formattedTime += "0";  // Tambahkan "0" di depan jika jam kurang dari 10
  }
  formattedTime += String(hours) + ":";  // Tambahkan jam ke dalam string
  if (minutes < 10) {
    formattedTime += "0";  // Tambahkan "0" di depan jika menit kurang dari 10
  }
  formattedTime += String(minutes);  // Tambahkan menit ke dalam string

  // Kirim waktu ke widget V5 di Blynk
  Blynk.virtualWrite(V8, formattedTime);

  // Tampilkan waktu di Serial Monitor (opsional)
  Serial.print("Jadwal Pakan Pagi Ditetapkan : ");
  Serial.println(formattedTime);
}

// Fungsi yang dipanggil ketika nilai dari widget Blynk V6 diubah
BLYNK_WRITE(V6) {
  // Membaca nilai waktu dari widget Blynk V6 dan menyimpannya dalam string
  String waktuMalamString = param.asString();
  // Memanggil fungsi parseTimeString() untuk memisahkan jam dan menit dari string waktu malam
  parseTimeString(waktuMalamString, malam_hours, malam_minutes);
}

// Fungsi yang dipanggil ketika nilai dari widget Blynk V7 diubah
BLYNK_WRITE(V7) {
  // Membaca nilai waktu dari widget Blynk V7 dan menyimpannya dalam string
  String waktuSoreString = param.asString();
  // Memanggil fungsi parseTimeString() untuk memisahkan jam dan menit dari string waktu sore
  parseTimeString(waktuSoreString, sore_hours, sore_minutes);
}

// Fungsi yang dipanggil ketika nilai dari widget Blynk V8 diubah
BLYNK_WRITE(V8) {
  // Membaca nilai waktu dari widget Blynk V8 dan menyimpannya dalam string
  String waktuPagiString = param.asString();
  // Memanggil fungsi parseTimeString() untuk memisahkan jam dan menit dari string waktu pagi
  parseTimeString(waktuPagiString, pagi_hours, pagi_minutes);
}




// Fungsi untuk memisahkan string waktu menjadi jam dan menit, dan menyimpannya dalam variabel yang sesuai
void parseTimeString(String timeString, int &hours, int &minutes) {
  // Mencari indeks dari karakter ':' dalam string waktu
  int colonIndex = timeString.indexOf(':');
  // Mengambil bagian jam dari string waktu, mengonversi ke integer, dan menyimpannya dalam variabel hours
  hours = timeString.substring(0, colonIndex).toInt();
  // Mengambil bagian menit dari string waktu, mengonversi ke integer, dan menyimpannya dalam variabel minutes
  minutes = timeString.substring(colonIndex + 1).toInt();
}


void loop() {
  Blynk.run();
  timeClient.update();  // Perbarui waktu dari server NTP
  unsigned long epochTime = timeClient.getEpochTime();
  time_t epochTime_t = (time_t)epochTime;  // Konversi epochTime menjadi time_t
  // Ubah waktu epoch menjadi waktu lokal
  struct tm localTime;
  localtime_r(&epochTime_t, &localTime);
  char formattedTime[20];
  unsigned long now = millis();
  int buttonState = digitalRead(buttonPin);
  int distance = sonar.ping_cm();
  if (buttonState == HIGH) {
    // Toggle the switch status
    autostatus = !autostatus;
  }
  float capacity_percentage = (distance / (float)MAX_DISTANCE) * 100.0;
  if (now - ledbefore >= 5000) {
    Blynk.virtualWrite(V1, LOW);
    ledbefore = now;
  }
  if (now - before >= interval) {

    Blynk.virtualWrite(V13, String(capacity_percentage));
    strftime(formattedTime, sizeof(formattedTime), "%d-%m-%y %H:%M:%S", &localTime);
    Blynk.virtualWrite(V5, String(formattedTime));  // Mengirim waktu ke widget V5 di Blynk
    // Mengambil jam, menit, dan detik dari struktur tm waktu lokal
    int hours = localTime.tm_hour;
    int minutes = localTime.tm_min;
    int seconds = localTime.tm_sec;
    String TImesnow = String(hours) + ":" + String(minutes) + ":" + String(seconds);
    Serial.println(TImesnow);
    // Menghitung jumlah detik pada waktu yang ditetapkan dari Blynk untuk pagi
    int pagicurrentSeconds = hours * 3600 + minutes * 60 + seconds;
    int pagiblynkSeconds = pagi_hours * 3600 + pagi_minutes * 60;
    // Menghitung jumlah detik pada waktu yang ditetapkan dari Blynk untuk sore
    int sorecurrentSeconds = hours * 3600 + minutes * 60 + seconds;
    int soreblynkSeconds = sore_hours * 3600 + sore_minutes * 60;
    // Menghitung jumlah detik pada waktu yang ditetapkan dari Blynk untuk malam
    int malamcurrentSeconds = hours * 3600 + minutes * 60 + seconds;
    int malamblynkSeconds = malam_hours * 3600 + malam_minutes * 60;

    if (autostatus) {
      // Serial.println("ON");
      digitalWrite(led_statuspin, HIGH);
      led_stat.setColor(BLYNK_BLUE);
      led_stat.on();
      // Membandingkan waktu Jadwal Blynk dengan waktu saat ini
      if (abs(pagicurrentSeconds - pagiblynkSeconds) <= 5) {
        if (!pakan_pagi) {
          led_pagi.setColor(BLYNK_BLUE);
          led_pagi.on();
          Serial.println("Pakan Pagi Diberikan");
          // Gerakkan servo ke posisi 90 derajat
          myServo.write(90);
          delay(1000); // Tunggu 1 detik
          myServo.write(0);
          delay(1000); // Tunggu 1 detik
          pakan_pagi = true;
          pakan_malam = false;
        }
      } else if (abs(sorecurrentSeconds - soreblynkSeconds) <= 5) {
        if (!pakan_sore) {
          led_sore.setColor(BLYNK_BLUE);
          led_sore.on();
          Serial.println("Pakan Sore Diberikan");
          // Gerakkan servo ke posisi 90 derajat
          myServo.write(90);
          delay(1000); // Tunggu 1 detik
          myServo.write(0);
          delay(1000); // Tunggu 1 detik
          pakan_sore = true;
          pakan_pagi = false;
        }
      } else if (abs(malamcurrentSeconds - malamblynkSeconds) <= 5) {
        if (!pakan_malam) {
          led_malam.setColor(BLYNK_BLUE);
          led_malam.on();
          Serial.println("Pakan Malam Diberikan");
          // Gerakkan servo ke posisi 90 derajat
          myServo.write(90);
          delay(1000); // Tunggu 1 detik
          myServo.write(0);
          delay(1000); // Tunggu 1 detik
          pakan_malam = true;
          pakan_sore = false;
        }
      }
    } else {
      digitalWrite(led_statuspin, LOW);
      led_stat.setColor(BLYNK_RED);
      led_stat.on();
      // Serial.println("OFF");
    }
    before = now;
  }

  if (now - resetbefore >= resetinterval) {
    // Menghitung waktu saat ini dalam detik
    int hours = localTime.tm_hour;
    int minutes = localTime.tm_min;
    int seconds = localTime.tm_sec;
    int currentSeconds = hours * 3600 + minutes * 60 + seconds;
    // Memeriksa apakah waktu saat ini adalah pukul 00:00 atau jam 12 malam
    if (hours == 0 && minutes == 0 && seconds == 0) {
      // Mereset semua lampu menjadi mati (off)
      led_pagi.setColor(BLYNK_RED);
      led_sore.setColor(BLYNK_RED);
      led_malam.setColor(BLYNK_RED);

      led_pagi.off();   // Matikan lampu pagi
      led_sore.off();   // Matikan lampu sore
      led_malam.off();  // Matikan lampu malam

      // Mengatur ulang status pemberian pakan
      pakan_pagi = false;
      pakan_sore = false;
      pakan_malam = false;
    }
    resetbefore = now;
  }
}