#include <string.h>

#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "lwip/sockets.h"

#include "types.hpp"

internal constexpr char kTagUdpLogger[] = "UDP_LOG";

internal int s_udp_log_socket = -1;
internal struct sockaddr_in s_udp_dest_addr;
internal vprintf_like_t s_original_vprintf = NULL;

internal int udp_logging_vprintf(const char *fmt, va_list ap)
{
  char buf[512];
  va_list ap_copy;
  va_copy(ap_copy, ap);
  int len = vsnprintf(buf, sizeof(buf) - 1, fmt, ap_copy);
  va_end(ap_copy);

  if (len > 0)
  {
    if (len >= sizeof(buf))
    {
      len = sizeof(buf) - 1;
    }
    buf[len] = '\0';
    if (s_udp_log_socket >= 0)
    {
      sendto(s_udp_log_socket, buf, len, 0, (struct sockaddr *)&s_udp_dest_addr,
             sizeof(s_udp_dest_addr));
    }
  }
  return s_original_vprintf(fmt, ap);
}

internal void start_udp_logging(int port)
{
  s_udp_log_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
  if (s_udp_log_socket < 0)
  {
    ESP_LOGE(kTagUdpLogger, "Unable to create socket: errno %d", errno);
    return;
  }

  int opt_val = 1;
  setsockopt(s_udp_log_socket, SOL_SOCKET, SO_BROADCAST, &opt_val,
             sizeof(opt_val));

  s_udp_dest_addr.sin_family = AF_INET;
  s_udp_dest_addr.sin_port = htons(port);

#ifdef CONFIG_CALENDINK_UDP_LOG_TARGET_IP
  if (strlen(CONFIG_CALENDINK_UDP_LOG_TARGET_IP) > 1)
  {
    s_udp_dest_addr.sin_addr.s_addr =
        inet_addr(CONFIG_CALENDINK_UDP_LOG_TARGET_IP);
  }
  else
  {
    s_udp_dest_addr.sin_addr.s_addr = inet_addr("255.255.255.255");
  }
#else
  s_udp_dest_addr.sin_addr.s_addr = inet_addr("255.255.255.255");
#endif

  s_original_vprintf = esp_log_set_vprintf(&udp_logging_vprintf);
  ESP_LOGI(kTagUdpLogger, "UDP logging broadcast started on port %d", port);
}
