/*
 * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "driver/gpio.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "nvs_flash.h"
#include <inttypes.h>
#include <sys/socket.h>

#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
#include "esp_mac.h"
#endif

#include "esp_bridge.h"
#include "esp_mesh_lite.h"

#define UART0_RX_tskPRIORITY (1)
#define UART1_RX_tskPRIORITY (2)

#define MAX_DATA_LEN (250 - 1)
#define UART_BUF_SIZE (1024)

#define UART0_BAUD_RATE 115200
#define UART0_TX_IO UART_PIN_NO_CHANGE
#define UART0_RX_IO UART_PIN_NO_CHANGE

#define UART1_BAUD_RATE 115200
#define UART1_TX_IO (GPIO_NUM_7)
#define UART1_RX_IO (GPIO_NUM_6)

static const char* TAG = "no_router";

/**
 * @brief Timed printing system information
 */
static void print_system_info_timercb(TimerHandle_t timer)
{
    uint8_t primary = 0;
    uint8_t sta_mac[6] = { 0 };
    wifi_ap_record_t ap_info = { 0 };
    wifi_second_chan_t second = 0;
    wifi_sta_list_t wifi_sta_list = { 0x0 };

    if (esp_mesh_lite_get_level() > 1) {
        esp_wifi_sta_get_ap_info(&ap_info);
    }
    esp_wifi_get_mac(ESP_IF_WIFI_STA, sta_mac);
    esp_wifi_ap_get_sta_list(&wifi_sta_list);
    esp_wifi_get_channel(&primary, &second);

    ESP_LOGI(TAG, "System information, channel: %d, layer: %d, self mac: " MACSTR ", parent bssid: " MACSTR ", parent rssi: %d, free heap: %" PRIu32 "", primary,
        esp_mesh_lite_get_level(), MAC2STR(sta_mac), MAC2STR(ap_info.bssid),
        (ap_info.rssi != 0 ? ap_info.rssi : -120), esp_get_free_heap_size());

    for (int i = 0; i < wifi_sta_list.num; i++) {
        ESP_LOGI(TAG, "Child mac: " MACSTR, MAC2STR(wifi_sta_list.sta[i].mac));
    }
}

static esp_err_t esp_storage_init(void)
{
    esp_err_t ret = nvs_flash_init();

    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        // NVS partition was truncated and needs to be erased
        // Retry nvs_flash_init
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }

    return ret;
}

static void wifi_init(void)
{
    // Station
    wifi_config_t wifi_config;
    memset(&wifi_config, 0x0, sizeof(wifi_config_t));
    esp_bridge_wifi_set_config(WIFI_IF_STA, &wifi_config);

    // Softap
    snprintf((char*)wifi_config.ap.ssid, sizeof(wifi_config.ap.ssid), "%s", CONFIG_BRIDGE_SOFTAP_SSID);
    strlcpy((char*)wifi_config.ap.password, CONFIG_BRIDGE_SOFTAP_PASSWORD, sizeof(wifi_config.ap.password));
    wifi_config.ap.channel = CONFIG_MESH_CHANNEL;
    esp_bridge_wifi_set_config(WIFI_IF_AP, &wifi_config);
}

int uart1_send_data(const char* data)
{
    int len = strlen(data);
    int tx_bytes = uart_write_bytes(UART_NUM_1, data, len);
    ESP_LOGI(TAG, "uart1 send %d bytes", tx_bytes);
    return tx_bytes;
}

/*process start from root node*/
static cJSON* broadcast_process(cJSON* payload, uint32_t seq)
{
    static uint32_t last_recv_seq, recv_cnt;
    if (last_recv_seq != seq) {
        /*recv data process*/
        cJSON* found = cJSON_GetObjectItem(payload, "data");
        ESP_LOGI(TAG, "broadcast info, count: %" PRIu32 ", size: %u", recv_cnt++, strlen((const char*)found->valuestring));
        uart1_send_data(found->valuestring);
        /*try to broadcast recv data*/
        cJSON* item = cJSON_CreateObject();
        if (item) {
            cJSON_AddStringToObject(item, "data", found->valuestring);
            esp_mesh_lite_try_sending_msg("broadcast", NULL, 0, item, &esp_mesh_lite_send_broadcast_msg_to_child);
            cJSON_Delete(item);
        }
        last_recv_seq = seq;
    }
    return NULL;
}

static cJSON* send_to_all_process(cJSON* payload, uint32_t seq)
{
    static uint32_t last_recv_seq, recv_cnt;
    if (last_recv_seq != seq) {
        cJSON* found = cJSON_GetObjectItem(payload, "data");
        ESP_LOGI(TAG, "send2all info, count: %" PRIu32 ", size: %u", recv_cnt++, strlen((const char*)found->valuestring));
        uart1_send_data(found->valuestring);
        /*try to broadcast recv data*/
        cJSON* item = cJSON_CreateObject();
        if (item) {
            cJSON_AddStringToObject(item, "data", found->valuestring);
            esp_mesh_lite_try_sending_msg("broadcast", NULL, 0, item, &esp_mesh_lite_send_broadcast_msg_to_child);
            cJSON_Delete(item);
        }
        last_recv_seq = seq;
    }
    return NULL;
}

static cJSON* send_to_all_ack_process(cJSON* payload, uint32_t seq)
{
    return NULL;
}

static const esp_mesh_lite_msg_action_t transport_action[] = {
    /* Report information to the root node */
    { "broadcast", NULL, broadcast_process },
    { "send2all", "send2all_ack", send_to_all_process },
    { "send2all_ack", NULL, send_to_all_ack_process },
    /* Must be NULL terminated */
    { NULL, NULL, NULL }
};

void mesh_send_internal(uint8_t* data, uint16_t len)
{
    if (NULL != data && len < MAX_DATA_LEN) {
        data[len] = '\0';
        cJSON* item = cJSON_CreateObject();
        if (item) {
            cJSON_AddStringToObject(item, "data", (char*)data);
#if CONFIG_MESH_ROOT
            esp_mesh_lite_try_sending_msg("broadcast", NULL, 0, item, &esp_mesh_lite_send_broadcast_msg_to_child);
#else
            esp_mesh_lite_try_sending_msg("send2all", "send2all_ack", 0, item, &esp_mesh_lite_send_msg_to_root);
#endif
            cJSON_Delete(item);
        }
    }
}

static void app_uart0_read_task(void* arg)
{
    uint32_t count = 0;
    uint8_t* data = (uint8_t*)malloc(UART_BUF_SIZE + 1);
    memset(data, 0, UART_BUF_SIZE);
    ESP_LOGI(TAG, "Uart0 read handle task is running");
    for (;;) {
        int len = uart_read_bytes(UART_NUM_0, data, MAX_DATA_LEN, pdMS_TO_TICKS(10));
        if (len > 0 && len < MAX_DATA_LEN) {
            ESP_LOGI(TAG, "uart0 recv, count: %" PRIu32 ", len: %u", count++, len);
            mesh_send_internal(data, len);
        }
    }
    ESP_LOGI(TAG, "Uart0 handle task is exit");
    free(data);
    vTaskDelete(NULL);
}

static void app_uart1_read_task(void* arg)
{
    uint32_t count = 0;
    uint8_t* data = (uint8_t*)malloc(UART_BUF_SIZE + 1);
    memset(data, 0, UART_BUF_SIZE);
    ESP_LOGI(TAG, "Uart1 read handle task is running");
    for (;;) {
        int len = uart_read_bytes(UART_NUM_1, data, MAX_DATA_LEN, pdMS_TO_TICKS(10));
        if (len > 0 && len < MAX_DATA_LEN) {
            ESP_LOGI(TAG, "uart1 recv, count: %" PRIu32 ", len: %u", count++, len);
            mesh_send_internal(data, len);
        }
    }
    ESP_LOGI(TAG, "Uart1 handle task is exit");
    free(data);
    vTaskDelete(NULL);
}

void app_uart_init(void)
{
    uart_config_t uart0_config = {
        .baud_rate = UART0_BAUD_RATE,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };
    ESP_ERROR_CHECK(uart_param_config(UART_NUM_0, &uart0_config));
    ESP_ERROR_CHECK(uart_set_pin(UART_NUM_0, UART0_TX_IO, UART0_RX_IO, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
    ESP_ERROR_CHECK(uart_driver_install(UART_NUM_0, 2 * UART_BUF_SIZE, 2 * UART_BUF_SIZE, 0, NULL, 0));
    xTaskCreate(app_uart0_read_task, "app_uart0_read_task", 2 * 1024, NULL, UART0_RX_tskPRIORITY, NULL);

    uart_config_t uart1_config = {
        .baud_rate = UART1_BAUD_RATE,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };
    ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart1_config));
    ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, UART1_TX_IO, UART1_RX_IO, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
    ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, 2 * UART_BUF_SIZE, 2 * UART_BUF_SIZE, 0, NULL, 0));
    xTaskCreate(app_uart1_read_task, "app_uart1_read_task", 2 * 1024, NULL, UART1_RX_tskPRIORITY, NULL);
}

void app_wifi_set_softap_info(void)
{
    char softap_ssid[32];
    uint8_t softap_mac[6];
    esp_wifi_get_mac(WIFI_IF_AP, softap_mac);
    memset(softap_ssid, 0x0, sizeof(softap_ssid));

#ifdef CONFIG_BRIDGE_SOFTAP_SSID_END_WITH_THE_MAC
    snprintf(softap_ssid, sizeof(softap_ssid), "%.25s_%02x%02x%02x", CONFIG_BRIDGE_SOFTAP_SSID, softap_mac[3], softap_mac[4], softap_mac[5]);
#else
    snprintf(softap_ssid, sizeof(softap_ssid), "%.32s", CONFIG_BRIDGE_SOFTAP_SSID);
#endif
    esp_mesh_lite_set_softap_ssid_to_nvs(softap_ssid);
    esp_mesh_lite_set_softap_psw_to_nvs(CONFIG_BRIDGE_SOFTAP_PASSWORD);
    esp_mesh_lite_set_softap_info(softap_ssid, CONFIG_BRIDGE_SOFTAP_PASSWORD);
}

void app_main()
{
    /**
     * @brief Set the log level for serial port printing.
     */
    esp_log_level_set("*", ESP_LOG_INFO);

    esp_storage_init();

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    esp_bridge_create_all_netif();

    wifi_init();

    esp_mesh_lite_config_t mesh_lite_config = ESP_MESH_LITE_DEFAULT_INIT();
    mesh_lite_config.join_mesh_ignore_router_status = true;
#if CONFIG_MESH_ROOT
    mesh_lite_config.join_mesh_without_configured_wifi = false;
#else
    mesh_lite_config.join_mesh_without_configured_wifi = true;
#endif
    esp_mesh_lite_init(&mesh_lite_config);

    app_wifi_set_softap_info();

#if CONFIG_MESH_ROOT
    ESP_LOGI(TAG, "Root node");
    esp_mesh_lite_set_allowed_level(1);
#else
    ESP_LOGI(TAG, "Child node");
#endif

    esp_mesh_lite_msg_action_list_register(transport_action);

    esp_mesh_lite_start();

    TimerHandle_t timer = xTimerCreate("print_system_info", 10000 / portTICK_PERIOD_MS,
        true, NULL, print_system_info_timercb);
    xTimerStart(timer, 0);

    app_uart_init();
}
