/* ESP drivers */
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_wifi.h"

/* FreeRTOS */
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"

/* App specific */
#include "app_wifi.h"

#define APP_WIFI_EVENT_GROUP_EVENT_CONNECTED (1 << 0U)
#define APP_WIFI_EVENT_GROUP_EVENT_FAILED    (1 << 1U)

#define APP_NETIF_NAME_PREFIX "LilyGo_EPD47"

#define APP_LOG_TAG "APP_WIFI"

EventGroupHandle_t g_app_wifi_event_group;

static uint8_t s_retries = 0U;

static void app_wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);

int app_wifi_init(void) {
    g_app_wifi_event_group = xEventGroupCreate();
    if (g_app_wifi_event_group == NULL) {
        return -1;
    }

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    esp_netif_t *sta_if = esp_netif_create_default_wifi_sta();

    wifi_init_config_t wifi_init_cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_cfg));

    char    hostname_buf[64];
    uint8_t mac_addr[6];
    esp_wifi_get_mac(WIFI_IF_STA, mac_addr);

    snprintf(hostname_buf, 64, APP_NETIF_NAME_PREFIX "_%02X%02X%02X", mac_addr[3], mac_addr[4], mac_addr[5]);

    esp_netif_set_hostname(sta_if, hostname_buf);

    esp_event_handler_instance_t inst_any_id;
    esp_event_handler_instance_t inst_got_ip;

    ESP_ERROR_CHECK(
        esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, app_wifi_event_handler, NULL, &inst_any_id));

    ESP_ERROR_CHECK(
        esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, app_wifi_event_handler, NULL, &inst_got_ip));

    wifi_config_t wifi_config = {
        .sta =
            {
                .ssid               = CONFIG_APP_WIFI_SSID,
                .password           = CONFIG_APP_WIFI_PASSWORD,
                .threshold.authmode = WIFI_AUTH_WPA2_PSK,
            },
    };

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(APP_LOG_TAG, "WiFi initialized, heap free: %ld.", esp_get_free_heap_size());

    return 0;
}

int app_wifi_is_ready(void) {
    if (xEventGroupGetBits(g_app_wifi_event_group) & APP_WIFI_EVENT_GROUP_EVENT_CONNECTED) {
        return 1;
    }

    return 0;
}

int app_wifi_wait_ready(uint32_t msec) {
    if (xEventGroupWaitBits(g_app_wifi_event_group, APP_WIFI_EVENT_GROUP_EVENT_CONNECTED, 0U, 0U,
                            pdMS_TO_TICKS(msec)) == pdTRUE) {
        return 0;
    }

    return -1;
}

static void app_wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
    /* Event handler */

    if (event_base == WIFI_EVENT) {
        if (event_id == WIFI_EVENT_STA_START) {
            esp_wifi_connect();
        }

        if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
            if (s_retries < CONFIG_APP_MAXIMUM_RETRY) {
                esp_wifi_connect();
                s_retries++;

                ESP_LOGI(APP_LOG_TAG, "Connection lost, retrying #%d/%d...", s_retries, CONFIG_APP_MAXIMUM_RETRY);
            } else {
                xEventGroupSetBits(g_app_wifi_event_group, APP_WIFI_EVENT_GROUP_EVENT_FAILED);

                ESP_LOGW(APP_LOG_TAG, "Connection lost, maximum retries reached.");
            }
        }
    } else if (event_base == IP_EVENT) {
        if (event_id == IP_EVENT_STA_GOT_IP) {
            xEventGroupSetBits(g_app_wifi_event_group, APP_WIFI_EVENT_GROUP_EVENT_CONNECTED);
            s_retries = 0U;

            ip_event_got_ip_t *event = event_data;

            ESP_LOGI(APP_LOG_TAG, "Connected, IP address: " IPSTR, IP2STR(&event->ip_info.ip));
        }
    }
}
