#define BLYNK_PRINT Serial
#define BLYNK_TEMPLATE_ID     "TMPL6id6EFANY"
#define BLYNK_TEMPLATE_NAME   "bel otomatis "
#define BLYNK_AUTH_TOKEN      "ttKmFPFxii2lHuonY7gPI551HQUqGPX1"

//library
#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#include <Wire.h>
#include <RTClib.h>
#include <LiquidCrystal_I2C.h>
// #include <SoftwareSerial.h>
// #include <DFRobotDFPlayerMini.h>
#include <EEPROM.h>
#include <Vector.h>

//wifi dan blynk
char auth[] = BLYNK_AUTH_TOKEN;
char ssid[] = "Wokwi-GUEST"; //nama hotspot yang digunakan
char pass[] = ""; //password hotspot yang digunakan

//blynk
BlynkTimer timer;
WidgetTerminal terminal(V6)

//dfplayer
// EspSoftwareSerial mp3Serial(16, 17); // rx, tx
// DFRobotDFPlayerMini dfplayer;

//rtc dan lcd
RTC_DS1307 rtc;
LiquidCrystal_I2C lcd (0x27, 16, 2);

// eeprom
#define EEPROM_SIZE 1024
#define MAX_JADWAL 50

//led dan buzzer
const int ledWiFi = 2; // LED indikator WiFi (GPIO2)
const int buzzer = 4; // buzzer jadwal aktif (GPIO4)

//rtc
unsigned long previousMillis = 0;
const long interval = 1000; //update tiap detik
char tujuhHari[7][12] = {"Senin", "Selasa", "Rabu", "Kamis", "Jum'at", "Sabtu", "Ahad"};

//input dari blynk
int inputHari = 0, inputJam = 0, inputMenit = 0, inputFile = 0; 
int inputIndex = - 1;

//struktur data jadwal
struct Jadwal {
  int hari;
  int jam;
  int menit;
  int file;
};
Jadwal jadwalList[MAX_JADWAL];
int totalJadwal = 0;

//blynk virtual pin
#define VP_STATUS_SISTEM V0
#define VP_HARI V1
#define VP_JAM V2
#define VP_MENIT V3
#define VP_FILE V4
#define VP_TAMBAH_JADWAL V5
#define VP_TERMINAL V6
#define VP_INDEX V7
#define VP_HAPUS_JADWAL V8
#define VP_FILE_UJI V9
#define VP_PUTAR_FILE V10

void setup() {
  Serial.begin(115200); //mengaktifkan serial monitor

  //inisialisasi pin
  pinMode(ledWiFi, OUTPUT); 
  pinMode(buzzer, OUTPUT);

  // Inisialisasi LCD
  lcd.init();
  lcd.backlight();
  
// koneksi wifi
  WiFi.begin("Wokwi-GUEST", "", 6);
  Serial.print("Connecting to WiFi");
  lcd.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
    for (int positionCounter = 0; positionCounter < 30; positionCounter++) {
      delay(250);
      lcd.scrollDisplayLeft();
    }
    lcd.clear();
  }
  Serial.println(" Connected!");
  lcd.print(" Connected!");
  digitalWrite(ledWiFi, HIGH); //nyalakan led
  tone(buzzer, 100); //play buzzer
  delay(1000);
  digitalWrite(ledWiFi, LOW); //matikan led
  noTone(buzzer); //matikan buzzer

//koneksi blynk
  Blynk.begin(auth, ssid, pass); //koneksi ke blynk

//koneksi rtc
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    abort();
  }
   Serial.println("RTC berhasil diatur!");

// Inisialisasi EEPROM
  EEPROM.begin(1024); 
  muatJadwalTampilkan(); //panggil fungsi muat jadwal dari eeprom dan tampilkan di terminal

// //inisialisasi dfplayer
//   mp3Serial.begin();
//   dfplayer.begin();
//   dfplayer.volume(20);
}

void loop() {
//tampilkan waktu realtime di lcd
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval){
  previousMillis = currentMillis;
  DateTime now = rtc.now(); //rtc berjalan
  lcd.setCursor(0, 0);
  lcd.print(String(tujuhHari[now.dayOfTheWeek()])+", ");
  Serial.println(String(tujuhHari[now.dayOfTheWeek()])+", "+String(now.day())+'/'+String(now.month())+'/'+String(now.year() % 100));
  lcd.setCursor(8, 0);
  lcd.print(String(now.day())+'/'+String(now.month())+'/'+String(now.year() % 100));
  lcd.setCursor(8, 1);
  lcd.print(String(now.hour())+':'+String(now.minute())+':'+String(now.second()));
  Serial.println(String(now.hour())+':'+String(now.minute())+':'+String(now.second()));
}

//jalankan fungsi lain
Blynk.run(); //blynk berjalan
timer.run();

// panggil fungsi
cekJadwalBunyikan(); //panggil fungsi cek jadwal dari eeprom dan bunyikan bel jika sesuai realtime.
delay(1000);
}

//muat jadwal dari eeprom dan tampilkan di terminal
void muatJadwalTampilkan() {
  terminal.clear(); //membersihkan terminal
  totalJadwal = 0;
  for (int i  = 0; i < MAX_JADWAL; i++) {
    Jadwal data;
    EEPROM.get(i * sizeof(Jadwal), data);

    //validasi hanya simpan jadwal yang masuk akal
    if (data.hari >= 1 && data.hari <= 7 && data.jam >= 0 && data.jam <= 24 && data.menit >= 0 && data.menit <= 60 ) {
      jadwalList[totalJadwal++] = data;

      char buffer[50];
      snprintf(buffer, sizeof(buffer), "Jadwal %d; Hari %d %02d;%02d - File %d", totalJadwal, data.hari, data.jam, data.menit, data.file);
      terminal.println(buffer);
    }
  }
  if (totalJadwal == 0) {
    terminal.println("belum ada jadwal tersimpan");
  }
  terminal.flush(); //tampilkan di terminal blynk
}

// tambah jadwal
BLYNK_WRITE(VP_HARI) {inputHari = param.asInt();}
BLYNK_WRITE(VP_JAM) {inputJam = param.asInt();}
BLYNK_WRITE(VP_MENIT) {inputMenit = param.asInt();}
BLYNK_WRITE(VP_FILE) {inputFile = param.asInt();}
BLYNK_WRITE(VP_TAMBAH_JADWAL) {
  if(param.asInt() == 1) {
  if (totalJadwal < MAX_JADWAL) {
    Jadwal j;
    j.hari = inputHari;
    j.jam = inputJam;
    j.menit = inputMenit;
    j.file = inputFile;
    jadwalList[totalJadwal++] = j;
    simpanJadwal();
   } else {
    terminal.println("jadwal penuh");
    Serial.println("jadwal penuh");
   }
  }
  terminal.flush();
}

//hapus jadwal
BLYNK_WRITE(VP_HAPUS_JADWAL) {
  if (inputIndex >= 0 && inputIndex < totalJadwal) {
    for (int i = inputIndex; i < totalJadwal -1; i++) {
      jadwalList[i] = jadwalList[i + 1];
    }
    totalJadwal--;
    simpanJadwal();
    muatJadwalTampilkan();
    } else {
      terminal.println("index tidak valid");
      Serial.println("index tidak valid");
    }
    terminal.flush();
  }

//putar file untuk uji suara
// BLYNK_WRITE(VP_PUTAR_FILE) {
//   if (param.asInt() == 1) {
//     int file =  Blynk.virtualRead(VP_PUTAR_FILE).asInt();
//     dfplayer.play(file);
//   }
// }

//cek jadwal dan bunyikan
void cekJadwalBunyikan(){
  DateTime now = rtc.now(); //rtc berjalan
  for (int i = 0; i < totalJadwal; i++) {
    if (jadwalList[i].hari == now.dayOfTheWeek() &&
        jadwalList[i].jam == now.hour() &&
        jadwalList[i].menit == now.minute()) {
      lcd.setCursor(0, 0);
      lcd.print("bel aktif    ");
      Serial.println("bel aktif    ");
      tone(buzzer, 100); //play buzzer
      delay(1000);
      noTone(buzzer); //matikan buzzer
      delay(60000); //hindari bunyi ulang dalam 1 menit
      break;
        }
    }
}

//simpan jadwal ke eeprom
void simpanJadwal(){
  EEPROM.write(0, totalJadwal);
  int addr = 1;
  for (int i = 0; i<totalJadwal; i++) {
    EEPROM.write(addr++, jadwalList[i].hari);
    EEPROM.write(addr++, jadwalList[i].jam);
    EEPROM.write(addr++, jadwalList[i].menit);
    EEPROM.write(addr++, jadwalList[i].file);
  }
  EEPROM.commit();
  Serial.println("jadwal disimpan ke epprom");
}

GND5VSDASCLSQWRTCDS1307+