#include "dhcp.h"

#include <DHCP/dhcp.h>
#include <socket.h>

#include "debug.h"
#include "systick.h"
#include "w5500.h"

static volatile dhcp_state_t dhcp_state = DHCP_STATE_INIT;
static volatile uint32_t dhcp_lease_time = 0;
static volatile uint32_t dhcp_last_time = 0;
static volatile uint32_t dhcp_retry_count = 0;
static uint32_t last_check_time = 0;

// Buffers
static uint8_t dhcp_buffer[512];
static wiz_NetInfo current_net_info = {
    .mac = {0xEA, 0x11, 0x22, 0x33, 0x44, 0xEA}, .dhcp = NETINFO_DHCP};

static void update_network_config(void) {
  wizchip_setnetinfo(&current_net_info);
  DEBUG_PRINT("IP config: %d.%d.%d.%d\n", current_net_info.ip[0],
              current_net_info.ip[1], current_net_info.ip[2],
              current_net_info.ip[3]);
}

void callback_ip_assigned(void) {
  dhcp_lease_time = getDHCPLeasetime();
  DEBUG_PRINT("IP newly assigned! Lease time: %lu sec\n", dhcp_lease_time);

  getIPfromDHCP(current_net_info.ip);
  getGWfromDHCP(current_net_info.gw);
  getSNfromDHCP(current_net_info.sn);
  getDNSfromDHCP(current_net_info.dns);

  dhcp_state = DHCP_STATE_LEASED;
}

void callback_ip_updated(void) {
  dhcp_lease_time = getDHCPLeasetime();
  DEBUG_PRINT("IP lease updated! New lease time: %lu sec\n", dhcp_lease_time);

  getIPfromDHCP(current_net_info.ip);
  getGWfromDHCP(current_net_info.gw);
  getSNfromDHCP(current_net_info.sn);
  getDNSfromDHCP(current_net_info.dns);

  dhcp_state = DHCP_STATE_LEASED;
}

void callback_ip_conflict(void) {
  DEBUG_PRINT("IP conflict!\n");
  dhcp_state = DHCP_STATE_INIT;
  dhcp_retry_count = 0;
}

void dhcp_init(void) {
  setSHAR(current_net_info.mac);
  DHCP_init(DHCP_SOCKET, dhcp_buffer);
  reg_dhcp_cbfunc(callback_ip_assigned, callback_ip_updated,
                  callback_ip_conflict);
  dhcp_state = DHCP_STATE_INIT;
  dhcp_retry_count = 0;
}

void dhcp_process(void) {
  uint32_t current_time = millis();

  // 500ms processing
  if ((current_time - last_check_time) >= 500) {
    last_check_time = current_time;

    switch (dhcp_state) {
      case DHCP_STATE_INIT:
        DHCP_run();
        if (dhcp_state == DHCP_STATE_LEASED) {
          update_network_config();
        }
        break;

      case DHCP_STATE_LEASED:
        // renew @ 50% of lease time
        if (current_time - dhcp_last_time >= (dhcp_lease_time * 500)) {
          dhcp_state = DHCP_STATE_RENEW;
          DHCP_run();
        }
        break;

      case DHCP_STATE_RENEW:
        DHCP_run();
        if (dhcp_state == DHCP_STATE_LEASED) {
          update_network_config();
          dhcp_last_time = current_time;
        }
        break;

      default:
        DHCP_run();
        break;
    }
  }
}

uint8_t dhcp_get_state(void) { return dhcp_state; }