#include <ArduinoJson.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <time.h>


// put in your wifi credentials
const char* ssid = "YourNetwork"; 
const char* password = "YourNetworkPassword"; 

//Your Domain name with URL path or IP address with path
// Be sure to paste in the Onestop ID for your desired stop, and your API key
String serverName = "https://transit.land/api/v2/rest/stops/YOUR_STOP'S_ONESTOP_ID/departures?apikey=PASTE_YOUR_API_KEY_HERE&relative_date=today&start_time=";

// the following vars are unsigned longs because the time, measured in ms, will become bigger # than can be stored in an int.
unsigned long lastTime = 0;

// // Set timer to 5 seconds (5000)
unsigned long timerDelay = 5000;

WiFiClientSecure client;
char currentTime[9]; // Buffer to hold HH:MM:SS

void setup() {
  Serial.begin(115200); 
  WiFi.begin(ssid, password);
  Serial.println("Connecting");
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to WiFi network with IP Address: ");
  Serial.println(WiFi.localIP());
 
  Serial.println("Timer set to 5 seconds (timerDelay variable), it will take 5 seconds before publishing the first reading.");
}

void loop() {
  //Send an HTTP POST request at timerDelay interval (in this case, 5 seconds)
  if ((millis() - lastTime) > timerDelay) {
    //Check WiFi connection status
    if(WiFi.status()== WL_CONNECTED){
      HTTPClient http;

      // Configure NTP
      configTime(-5 * 3600, 0, "pool.ntp.org", "time.nist.gov"); // UTC-5 (Eastern Time)
      Serial.println();
      Serial.println("Attempting to obtain time...");
      obtainTime(); // Call the function to obtain time

      // Retrieve current time (e.g., 09:30:00)
      struct tm timeinfo;
      if (!getLocalTime(&timeinfo)) {
        Serial.println("Failed to obtain time");
        return;
      }
  
      strftime(currentTime, sizeof(currentTime), "%H:%M:%S", &timeinfo);

      // // Print current time for debugging
      // Serial.print("Current Time: ");
      // Serial.println(currentTime);

      // call API, while passing the current time :D 
      String serverPath = serverName + currentTime;
      
      // Your Domain name with URL path or IP address with path
      http.begin(serverPath.c_str());
      
      // Send HTTP GET request
      int httpResponseCode = http.GET();
      
      if (httpResponseCode>0) {
        // Serial.println();
        // Serial.print("HTTP Response code: ");
        // Serial.println(httpResponseCode);
        String payload = http.getString();
        // Serial.println();
        // Serial.print("Full payload: ");
        // Serial.println(payload);
        parseAndFilterJSON(payload);
      }
      else {
        Serial.print("Error code: ");
        Serial.println(httpResponseCode);
      }
      // Free resources
      http.end();
    }
    else {
      Serial.println("WiFi Disconnected");
    }
    lastTime = millis();
  }
}

void parseAndFilterJSON(String input){
// Stream & input;
  JsonDocument filter;

  JsonObject filter_stops_0_departures_0 = filter["stops"][0]["departures"].add<JsonObject>();
  filter_stops_0_departures_0["departure_time"] = true;

  JsonObject filter_stops_0_departures_0_trip = filter_stops_0_departures_0["trip"].to<JsonObject>();
  filter_stops_0_departures_0_trip["block_id"] = true;
  filter_stops_0_departures_0_trip["direction_id"] = true;
  filter_stops_0_departures_0_trip["id"] = true;

  JsonObject filter_stops_0_departures_0_trip_route = filter_stops_0_departures_0_trip["route"].to<JsonObject>();
  filter_stops_0_departures_0_trip_route["id"] = true;
  filter_stops_0_departures_0_trip_route["onestop_id"] = true;
  filter_stops_0_departures_0_trip_route["route_id"] = true;
  filter_stops_0_departures_0_trip_route["route_short_name"] = true;
  filter_stops_0_departures_0_trip_route["route_type"] = true;
  filter_stops_0_departures_0_trip["stop_pattern_id"] = true;
  filter_stops_0_departures_0_trip["trip_headsign"] = true;
  filter_stops_0_departures_0_trip["trip_id"] = true;
  filter_stops_0_departures_0_trip["trip_short_name"] = true;

  JsonDocument doc;

  DeserializationError error = deserializeJson(doc, input, DeserializationOption::Filter(filter));

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




  // Track the next departure
  const char* nextDepartureTime = nullptr;
  const char* nextDirection = nullptr;
  const char* nextRouteId = nullptr;

  // for departure in departures (FYI the call only returns the next 20 departures)
  for (JsonObject stops_0_departure : doc["stops"][0]["departures"].as<JsonArray>()) {

    // define the variables we might need:
    // time variables
    const char* departure_time = stops_0_departure["departure_time"]; // "09:08:00", ...
    //define trip variables:
    JsonObject stops_0_departure_trip = stops_0_departure["trip"];
    const char* block_id = stops_0_departure_trip["block_id"]; // "42", "41", "41", ...
    int direction_id = stops_0_departure_trip["direction_id"]; // 1, 0, 1, 0, 1, 0, ...
    const char* direction = (direction_id == 0) ? "S" : "N"; // assign 0 = S, 1 = N
    long long departure_trip_id = stops_0_departure_trip["id"]; // 5420038252, 5420038264, ...
    // route variables
    JsonObject stops_0_departure_trip_route = stops_0_departure_trip["route"];
    long trip_route_id = stops_0_departure_trip_route["id"]; // 61766253, 61766253, ...
    const char* route_onestop_id = stops_0_departure_trip_route["onestop_id"];
    const char* route_id = stops_0_departure_trip_route["route_id"]; // AS, ER, RES, RW, RWS, SB, SG, SV (https://www.transit.land/operators/o-dr5r-nycferry#routes)
    const char* route_short_name = stops_0_departure_trip_route["route_short_name"]; // same as above
    int route_type = stops_0_departure_trip_route["route_type"]; // 4, 4, 4, 4, ... (indicates ferry vehicle)
    // stop variables
    int stop_pattern_id = stops_0_departure_trip["stop_pattern_id"]; // 7, 8, 7, 8, ...
    const char* strip_headsign = stops_0_departure_trip["trip_headsign"]; // "ER... (the boat's actual headsign)
    const char* trip_id = stops_0_departure_trip["trip_id"]; // "2114", "2126", ...
    const char* trip_short_name = stops_0_departure_trip["trip_short_name"]; // 1101...


    // Print current time and departure time for debugging
    Serial.print("Current Time: ");
    Serial.println(currentTime);
    Serial.print("Departure Time: ");
    Serial.println(departure_time);

    // Compare departure_time with currentTime
    if (strcmp(departure_time, currentTime) > 0) {
      nextDepartureTime = departure_time;
      nextDirection = direction;
      nextRouteId = route_id;
      break; // Exit loop once the next departure is found
    }
  }

  if (nextDepartureTime != nullptr) {
    Serial.println("Next Departure Details:");
    Serial.print("*Route ID: ");
    Serial.println(nextRouteId);
    Serial.print("*Direction: ");
    Serial.println(nextDirection);
    Serial.print("*Departure Time: ");
    Serial.println(nextDepartureTime);
  } 
  else 
  {
    Serial.println("No upcoming departures today.");
  }
}

void obtainTime() {
    struct tm timeinfo;
    if (!getLocalTime(&timeinfo)) {
        Serial.println("Failed to obtain time. Retrying...");
        delay(2000); // Wait 2 seconds before retrying
        obtainTime(); // Recursive retry
    } else {
        Serial.println("Time obtained successfully.");
        Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S"); // Print time for debugging
    }
}