// Learn about the ESP32 WiFi simulation in
// https://docs.wokwi.com/guides/esp32-wifi

#include <WiFi.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "RTClib.h"
#include <HTTPClient.h>
#include <ArduinoJson.h>

LiquidCrystal_I2C LCD = LiquidCrystal_I2C(0x27, 20, 4);
RTC_DS1307 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

#define NTP_SERVER     "au.pool.ntp.org"
#define UTC_OFFSET     0
#define UTC_OFFSET_DST 0

#define interruptPin GPIO_NUM_12

const char* ssid = "Wokwi-GUEST";
const char* password = "";

const int solenoids = 5;
const int motor = 18;
//const int interruptPin = 12;

String syncedTime;
String jsonOpenTime;
String jsonShutTime;

DateTime now;
const DateTime reSyncTime = DateTime(now.year(), now.month(), now.day(), 0, 0, 0);
DateTime shutTime;
DateTime openTime;

const String duskTimeApi = "https://api.sunrisesunset.io/json?lat=-35.06042984189702&lng=138.85387414368216&timezone=UTC&date=today";

//HW interrupt for when immediate entry is required
  static void IRAM_ATTR gpio_isr_handler(void* arg) {
  Serial.begin(115200);
  LCD.init();
  LCD.backlight();
  LCD.setCursor(0, 0);
  LCD.clear();
  LCD.println("INTERRUPT: PLEASE RESET DEVICE TO CONTINUE");
  //while(true)
  //{
    Serial.println("PLEASE RESET");
    digitalWrite(solenoids, LOW);
    digitalWrite(motor, LOW);
  //}
}

//Wifi connect spinner
void spinner() {
  static int8_t counter = 0;
  const char* glyphs = "\xa1\xa5\xdb";
  LCD.setCursor(15, 1);
  LCD.print(glyphs[counter++]);
  if (counter == strlen(glyphs)) {
    counter = 0;
  }
}

void syncLocalTime() {
  //uses UTP time server to sync time
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    LCD.setCursor(0, 1);
    LCD.println("Connection Err");
  }

  String AMorPM;
  if (timeinfo.tm_hour > 12) {
    AMorPM = "PM";
  } else {
    AMorPM = "AM";
  }

  //adjusts dateTime so time is always synced up properly
  rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));

  LCD.setCursor(0, 0);
  LCD.clear();
}

void button_config()
{
    gpio_install_isr_service(0);
    printf("configuring button\n");
    gpio_reset_pin(interruptPin);
    gpio_set_direction(interruptPin, GPIO_MODE_INPUT);
    gpio_pullup_en(interruptPin);
    gpio_set_intr_type(interruptPin, GPIO_INTR_POSEDGE);
    gpio_isr_handler_add(interruptPin, gpio_isr_handler, NULL);
    printf("config complete\n");
}

void syncOpenShutTime() {

  LCD.print("Fetching duskTime...");
  LCD.setCursor(0, 1);
  //connects to internet
  HTTPClient http;
  http.begin(duskTimeApi);
  
  //connects to api website
  int httpResponseCode = http.GET();
  if (httpResponseCode > 0) {
    //if everything goes right it'll proceed
    LCD.print("HTTP ");
    LCD.print(httpResponseCode);
    String payload = http.getString();
    Serial.println();
    Serial.println(payload);

    //converts json data into string
    //if errors deserializing check if size is too low
    DynamicJsonDocument doc(2048);
    DeserializationError error = deserializeJson(doc, payload);

    if (error) {
      Serial.print("deserializeJson() failed: ");
      Serial.println(error.c_str());
    }

    //first indicates object the times are inside of
    JsonObject results = doc["results"];
    //then sets variables as variable name
    jsonShutTime = results["dusk"].as<String>();
    Serial.println(jsonShutTime);
    jsonOpenTime = results["sunrise"].as<String>();
    Serial.println(jsonOpenTime);


    shutTime = convertToDateTime(jsonShutTime.c_str(), now);
    openTime = convertToDateTime(jsonOpenTime.c_str(), now);

    LCD.clear();
  }
  else
  {
    LCD.print("Connection error!");
    return;
  }

}

DateTime convertToDateTime(const char* timeStr, DateTime now) {
/*
  **************
  ****SYNTAX****
  **************

  strdup = duplicate string
  strtok = sets token variable to everything before the semicolon
  atoi = conversion from char to int
  free = free up unused memory
*/


  // Split the time string up into hours and minutes
  char* timeCopy = strdup(timeStr);
  char* token = strtok(timeCopy, " :");
  Serial.println(timeCopy);
  int hour = atoi(token);
  token = strtok(NULL, " :");
  int minute = atoi(token);
  token = strtok(NULL, " :");
  //important so it properly converts to 24 hour
  char* ampmToken = strtok(NULL, " :");

  // Trim leading and trailing spaces from ampmToken
  char* trimmedAmpm = strtok(ampmToken, " ");
  
  // Handle AM/PM
  bool isPM = (strcasecmp(trimmedAmpm, "PM") == 0);
  
  if (isPM && hour != 12) {
    hour += 12;
  } else if (!isPM && hour == 12) {
    hour = 0;
  }

  DateTime timeConverted(now.year(), now.month(), now.day(), hour, minute, 0); // Assuming seconds are always 0
  free(timeCopy);
  return timeConverted;
}

void setup() {
  Serial.begin(115200);
  pinMode(solenoids, OUTPUT);
  pinMode(motor, OUTPUT);
  button_config();
  LCD.init();
  LCD.backlight();
  LCD.setCursor(0, 0);
  LCD.print("Connecting to ");
  LCD.setCursor(0, 1);
  LCD.print(ssid);
  WiFi.begin(ssid, password, 6);
  while (WiFi.status() != WL_CONNECTED) {
    delay(250);
    spinner();
  }

  LCD.clear();
  LCD.setCursor(0,0);

  LCD.println("WiFi connected with IP:");
  LCD.setCursor(0,1);
  LCD.println(WiFi.localIP());

  delay(250);

  if (!rtc.begin()) {
    LCD.println("Couldn't find RTC");
    abort();
  }

  LCD.clear();
  LCD.setCursor(0, 0);
  LCD.println("Online");
  LCD.setCursor(0, 1);
  LCD.println("Updating time...");
  LCD.setCursor(0, 0);
  configTime(UTC_OFFSET, UTC_OFFSET_DST, NTP_SERVER);
  syncLocalTime();
  syncOpenShutTime();
  LCD.setCursor(0, 0);
  LCD.clear();
}

void loop() {
  now = rtc.now();
  LCD.setCursor(0, 0);
  if(now.hour() < 10){
  LCD.print('0');
  }
  LCD.print(now.hour(), DEC);
  LCD.print(':');
  if(now.minute() < 10){
  LCD.print('0');
  }
  LCD.print(now.minute(), DEC);
  LCD.print(':');
    if(now.second() < 10){
  LCD.print('0');
  }
  LCD.print(now.second(), DEC);
  /*
  LCD.print(" ");
  LCD.print(now.hour() % shutTime.hour(), DEC);
  LCD.print(':');
  LCD.print(now.minute() % shutTime.minute(), DEC);
  */
  LCD.setCursor(0, 1);
  LCD.print(shutTime.hour(), DEC);
  LCD.print(':');
  if(shutTime.minute() < 10){
  LCD.print('0');
  }
  LCD.print(shutTime.minute(), DEC);
  LCD.print(':');
  LCD.print(shutTime.second(), DEC);

  LCD.setCursor(0, 2);
  LCD.print(openTime.hour(), DEC);
  LCD.print(':');
  LCD.print(openTime.minute(), DEC);
  LCD.print(':');
  LCD.print(openTime.second(), DEC);
  
  if(now <= reSyncTime)
  {
    syncLocalTime();
    syncOpenShutTime();
  }

  if(now <= shutTime)
  {
    digitalWrite(solenoids, HIGH);
  }

    if(now >= openTime)
  {
    digitalWrite(solenoids, LOW);
    
    if(now==openTime){
    syncLocalTime();
    syncOpenShutTime();
    }
  }
}
GND5VSDASCLSQWRTCDS1307+
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module