#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "main.h"
#include "gpio.h"
#include "iwdg.h"

#include "config.h"
#include "debug_log.h"
#include "route_msg.h"
#include "app_runtime.h"
#include "task_net_poll.h"
#include "tcp_server.h"
#include "tcp_client.h"
#include "uart_trans.h"

QueueHandle_t xTcpRxQueue = NULL;
QueueHandle_t xConfigQueue = NULL;
QueueHandle_t xLinkTxQueues[CONFIG_LINK_COUNT] = {0};
SemaphoreHandle_t xNetSemaphore = NULL;

TaskHandle_t xUartRxTaskHandle = NULL;
TaskHandle_t xConfigTaskHandle = NULL;
volatile BaseType_t g_netif_ready = pdFALSE;
volatile uint32_t g_netif_phase = 0u;
volatile int32_t g_netif_add_err = 0x7FFFFFFF;
volatile int32_t g_netif_set_default_err = 0x7FFFFFFF;
volatile int32_t g_netif_set_link_down_err = 0x7FFFFFFF;
volatile int32_t g_netif_set_up_err = 0x7FFFFFFF;
volatile int32_t g_netif_init_ok = 0;

static TaskHandle_t xNetPollTaskHandle = NULL;
static TaskHandle_t xTcpSrvTaskS1Handle = NULL;
static TaskHandle_t xTcpSrvTaskS2Handle = NULL;
static TaskHandle_t xTcpCliTaskC1Handle = NULL;
static TaskHandle_t xTcpCliTaskC2Handle = NULL;
static TaskHandle_t xDefaultTaskHandle = NULL;
static BaseType_t xNetworkTasksStarted = pdFALSE;

void app_start_network_tasks(void)
{
#if !DIAG_TASK_ISOLATION
    BaseType_t rc;
    const device_config_t *cfg;

    if (xNetworkTasksStarted != pdFALSE) {
        debug_log_write("[NET] start-network-tasks already\r\n");
        return;
    }

    cfg = config_get();

    debug_log_printf("[NET] start-network-tasks enter free=%lu min=%lu\r\n",
                     (unsigned long)xPortGetFreeHeapSize(),
                     (unsigned long)xPortGetMinimumEverFreeHeapSize());

    if (cfg->links[CONFIG_LINK_S1].enabled != 0u) {
        rc = xTaskCreate(TcpSrvTask_S1, "TcpSrvS1", TASK_STACK_TCP_SERVER, NULL, TASK_PRIORITY_TCP_SERVER, &xTcpSrvTaskS1Handle);
        debug_log_printf("[NET] create TcpSrvS1 rc=%ld free=%lu min=%lu\r\n",
                         (long)rc,
                         (unsigned long)xPortGetFreeHeapSize(),
                         (unsigned long)xPortGetMinimumEverFreeHeapSize());
        configASSERT(rc == pdPASS);
    } else {
        debug_log_write("[NET] skip TcpSrvS1 en=0\r\n");
    }

    if (cfg->links[CONFIG_LINK_S2].enabled != 0u) {
        rc = xTaskCreate(TcpSrvTask_S2, "TcpSrvS2", TASK_STACK_TCP_SERVER, NULL, TASK_PRIORITY_TCP_SERVER, &xTcpSrvTaskS2Handle);
        debug_log_printf("[NET] create TcpSrvS2 rc=%ld free=%lu min=%lu\r\n",
                         (long)rc,
                         (unsigned long)xPortGetFreeHeapSize(),
                         (unsigned long)xPortGetMinimumEverFreeHeapSize());
        configASSERT(rc == pdPASS);
    } else {
        debug_log_write("[NET] skip TcpSrvS2 en=0\r\n");
    }

    if (cfg->links[CONFIG_LINK_C1].enabled != 0u) {
        rc = xTaskCreate(TcpCliTask_C1, "TcpCliC1", TASK_STACK_TCP_CLIENT, NULL, TASK_PRIORITY_TCP_CLIENT, &xTcpCliTaskC1Handle);
        debug_log_printf("[NET] create TcpCliC1 rc=%ld free=%lu min=%lu\r\n",
                         (long)rc,
                         (unsigned long)xPortGetFreeHeapSize(),
                         (unsigned long)xPortGetMinimumEverFreeHeapSize());
        configASSERT(rc == pdPASS);
    } else {
        debug_log_write("[NET] skip TcpCliC1 en=0\r\n");
    }

    if (cfg->links[CONFIG_LINK_C2].enabled != 0u) {
        rc = xTaskCreate(TcpCliTask_C2, "TcpCliC2", TASK_STACK_TCP_CLIENT, NULL, TASK_PRIORITY_TCP_CLIENT, &xTcpCliTaskC2Handle);
        debug_log_printf("[NET] create TcpCliC2 rc=%ld free=%lu min=%lu\r\n",
                         (long)rc,
                         (unsigned long)xPortGetFreeHeapSize(),
                         (unsigned long)xPortGetMinimumEverFreeHeapSize());
        configASSERT(rc == pdPASS);
    } else {
        debug_log_write("[NET] skip TcpCliC2 en=0\r\n");
    }

    xNetworkTasksStarted = pdTRUE;

    debug_log_printf("[NET] start-network-tasks exit free=%lu min=%lu\r\n",
                     (unsigned long)xPortGetFreeHeapSize(),
                     (unsigned long)xPortGetMinimumEverFreeHeapSize());
#endif
}

static void StartDefaultTask(void *argument)
{
    BaseType_t iwdg_ready = pdFALSE;

    (void)argument;
    debug_log_boot("default-task");

    if (MX_IWDG_Init() == HAL_OK) {
        debug_log_write("[BOOT] iwdg-started\r\n");
        iwdg_ready = pdTRUE;
    } else {
        debug_log_write("[BOOT] iwdg-init-fail\r\n");
    }

    for (;;) {
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
        if (iwdg_ready == pdTRUE) {
            HAL_IWDG_Refresh(&hiwdg);
        }
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

void MX_FREERTOS_Init(void)
{
    uint32_t i;

    route_msg_init();
    configASSERT(uart_trans_init() == 0);
    debug_log_boot("uart-trans-init");

    xNetSemaphore = xSemaphoreCreateBinary();
    xTcpRxQueue = xQueueCreate(6, sizeof(route_msg_t *));
    xConfigQueue = xQueueCreate(2, sizeof(route_msg_t *));
    for (i = 0; i < CONFIG_LINK_COUNT; ++i) {
        xLinkTxQueues[i] = xQueueCreate(4, sizeof(route_msg_t *));
    }

    configASSERT(xNetSemaphore != NULL);
    configASSERT(xTcpRxQueue != NULL);
    configASSERT(xConfigQueue != NULL);
    for (i = 0; i < CONFIG_LINK_COUNT; ++i) {
        configASSERT(xLinkTxQueues[i] != NULL);
    }

    configASSERT(xTaskCreate(StartDefaultTask, "defaultTask", TASK_STACK_DEFAULT, NULL, TASK_PRIORITY_DEFAULT, &xDefaultTaskHandle) == pdPASS);

    configASSERT(xTaskCreate(NetPollTask, "NetPoll", TASK_STACK_NET_POLL, NULL, TASK_PRIORITY_NET_POLL, &xNetPollTaskHandle) == pdPASS);
#if !DIAG_TASK_ISOLATION
    configASSERT(xTaskCreate(UartRxTask, "UartRx", TASK_STACK_UART_RX, NULL, TASK_PRIORITY_UART_RX, &xUartRxTaskHandle) == pdPASS);
    configASSERT(xTaskCreate(ConfigTask, "Config", TASK_STACK_CONFIG, NULL, TASK_PRIORITY_CONFIG, &xConfigTaskHandle) == pdPASS);
#else
    debug_log_write("[DIAG] task-isolation enabled\r\n");
#endif
    debug_log_boot("tasks-created");
}
