#include "w5500.h"

#include <DHCP/dhcp.h>
#include <DNS/dns.h>
#include <MQTT/MQTTClient.h>
#include <W5500/w5500.h>
#include <socket.h>
#include <string.h>

#include "ch32v003fun.h"
#include "config.h"
#include "debug.h"
#include "spi_dma.h"
#include "systick.h"

static uint8_t dns_buffer[512];

void configure_network(void) {
  DEBUG_PRINT("===\n");
  DEBUG_PRINT("Starting network configuration...\n");
  DEBUG_PRINT("===\n");

  // Setup chip select and SPI callbacks
  reg_wizchip_cs_cbfunc(spi_select, spi_unselect);
  // reg_wizchip_spi_cbfunc(spi_read_byte, spi_write_byte);
  reg_wizchip_spiburst_cbfunc(spidma_read_buffer, spidma_write_buffer);

  uint8_t rx_tx_buff_sizes[] = {2, 2, 2, 2, 2, 2, 2, 2};
  wizchip_init(rx_tx_buff_sizes, rx_tx_buff_sizes);
}

// todo: rm !!!
void resolve_domain_name(const char* domain_name) {
  DEBUG_PRINT("Resolving domain name \"%s\"...\n", domain_name);

  DNS_init(DNS_SOCKET, dns_buffer);
  // cloudflare dns
  uint8_t dns[] = {1, 1, 1, 1};
  uint8_t addr[4];
  int8_t res;
  uint8_t retries = 0;

  while (retries < 3) {
    Delay_Ms(250);

    res = DNS_run(dns, (uint8_t*)domain_name, addr);
    if (res == 1) {
      DEBUG_PRINT("Result: %d.%d.%d.%d\n", addr[0], addr[1], addr[2], addr[3]);
      break;
    } else {
      DEBUG_PRINT("DNS_run() failed, res = %d. Retries: %u\n", res, retries);
    }
    retries++;
  }
}

int setup_mqtt_client(Network* network, ch32_mqtt_options_t* opts,
                      MQTTClient* client) {
  static unsigned char tx_buffer[MQTT_TX_BUFFER_SIZE];
  static unsigned char rx_buffer[MQTT_RX_BUFFER_SIZE];
  int rc;
  uint8_t target_ip[] = MQTT_TARGET_IP;

  // Initialize network
  NewNetwork(network, TCP_SOCKET);
  rc = ConnectNetwork(network, target_ip, MQTT_TARGET_PORT);
  if (rc != SOCK_OK) {
    DEBUG_PRINT("Network connection failed: %d\n", rc);
    return -1;
  }

  // Initialize the MQTT client
  MQTTClientInit(client, network, MQTT_COMMAND_TIMEOUT_MS, tx_buffer,
                 sizeof(tx_buffer), rx_buffer, sizeof(rx_buffer));

  // Setup MQTT connection data
  MQTTPacket_connectData connect_data = MQTTPacket_connectData_initializer;
  connect_data.willFlag = 0;
  connect_data.MQTTVersion = 3;
  connect_data.clientID.cstring = opts->clientid;
  connect_data.username.cstring = opts->username;
  connect_data.password.cstring = opts->password;
  connect_data.keepAliveInterval = MQTT_KEEP_ALIVE_INTERVAL;
  connect_data.cleansession = 1;

  // Connect to MQTT broker
  rc = MQTTConnect(client, &connect_data);
  if (rc != 0) {
    DEBUG_PRINT("Failed to connect: %d\n", rc);
    return -2;
  }

  return 0;
}

int subscribe_to_topic(MQTTClient* client, const char* topic, enum QoS qos,
                       messageHandler message_callback) {
  int rc = MQTTSubscribe(client, topic, qos, message_callback);
  if (rc != 0) {
    DEBUG_PRINT("Failed to subscribe to %s: %d\n", topic, rc);
    return rc;
  }
  return 0;
}

void publish_message(MQTTClient* client, const char* payload,
                     const char* topic) {
  MQTTMessage message = {.qos = QOS0,
                         .retained = 0,
                         .dup = 0,
                         .payload = (void*)payload,
                         .payloadlen = strlen(payload)

  };

  if (MQTTPublish(client, topic, &message) != 0) {
    DEBUG_PRINT("Publish failed\n");
  } else {
    DEBUG_PRINT("Message published successfully\n");
  }
}