#include "NTPupdate.h"

// Sends an NTP request packet to the specified server address
void sendNTPpacket(IPAddress &address) {
  byte packetBuffer[NTP_PACKET_SIZE] = {0}; // Initialize buffer with zeros

  // Set NTP packet header fields as per NTP protocol
  packetBuffer[0] = 0b11100011; // LI, Version, Mode
  packetBuffer[2] = 6;          // Polling interval
  packetBuffer[3] = 0xEC;       // Peer clock precision

  // Root Delay & Root Dispersion fields
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;

  // Send the NTP request to port 123 (NTP standard port)
  Udp.beginPacket(address, 123);
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

// Retrieves the current time from an NTP server
time_t getNtpTime() {
  IPAddress ntpServerIP;
  byte packetBuffer[NTP_PACKET_SIZE];

  // Clear any previously received UDP packets
  while (Udp.parsePacket() > 0);

  // Resolve the NTP server's hostname to its IP address
  if (!WiFi.hostByName(ntpServerName, ntpServerIP)) {
    return 0; // Return 0 if hostname resolution fails
  }

  // Send an NTP request
  sendNTPpacket(ntpServerIP);

  // Wait for a response with a 1500ms timeout
  uint32_t startWait = millis();
  while (millis() - startWait < 1500) {
    if (Udp.parsePacket() >= NTP_PACKET_SIZE) { // Check if a valid packet is received
      Udp.read(packetBuffer, NTP_PACKET_SIZE);

      // Extract "seconds since 1900" from the packet (bytes 40-43)
      unsigned long secsSince1900 =
        ((unsigned long)packetBuffer[40] << 24) |
        ((unsigned long)packetBuffer[41] << 16) |
        ((unsigned long)packetBuffer[42] << 8)  |
        (unsigned long)packetBuffer[43];

      // Convert to UNIX epoch time (seconds since 1970)
      return secsSince1900 - 2208988800UL;
    }
  }

  // Return 0 if no valid response is received
  return 0;
}

// Updates the RTC with the time from an NTP server
void NTPupdate() {
  // Abort if Wi-Fi is not connected
  if (!wifi) {
    NTPupdated = false;
    return;
  }

  // Retrieve the current time from the NTP server
  time_t currentTime = getNtpTime();

  if (currentTime) {
    // Set the RTC if valid time is received
    rtc.setTime(currentTime);
    rtcset = true;
    NTPupdated = true;
    radio.rds.ctupdate = false;
  } else {
    // Indicate that the update failed
    NTPupdated = false;
    radio.rds.ctupdate = true;
  }
}