#include "task_net_poll.h"

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "CH390.h"
#include <string.h>

#if !DIAG_CH390_RAW_POLL
#include "lwip/tcpip.h"
#include "lwip/ip4_addr.h"
#endif

#include "ethernetif.h"
#include "config.h"
#include "app_runtime.h"
#include "debug_log.h"

#define CH390_RESTART_HOLD_DOWN_MS          500u
#define NETWORK_TASK_DELETE_SETTLE_MS       50u
#define CH390_EXPECTED_VENDOR_ID            0x1C00u
#define CH390_EXPECTED_PRODUCT_ID           0x9151u

static void net_poll_wait_for_network_tasks_stop(void)
{
    while (app_network_tasks_are_stopped() == pdFALSE) {
        vTaskDelay(pdMS_TO_TICKS(20));
    }
}

static BaseType_t net_poll_restart_network_stack(const device_config_t *cfg)
{
#if !DIAG_CH390_RAW_POLL
    ip4_addr_t ipaddr;
    ip4_addr_t netmask;
    ip4_addr_t gateway;
    uint16_t vendor_id;
    uint16_t product_id;
    uint8_t revision;

    IP4_ADDR(&ipaddr, cfg->net.ip[0], cfg->net.ip[1], cfg->net.ip[2], cfg->net.ip[3]);
    IP4_ADDR(&netmask, cfg->net.mask[0], cfg->net.mask[1], cfg->net.mask[2], cfg->net.mask[3]);
    IP4_ADDR(&gateway, cfg->net.gw[0], cfg->net.gw[1], cfg->net.gw[2], cfg->net.gw[3]);
#endif

    ethernetif_force_link_down();
    g_netif_ready = pdFALSE;
    app_request_network_task_stop();
    net_poll_wait_for_network_tasks_stop();
    vTaskDelay(pdMS_TO_TICKS(NETWORK_TASK_DELETE_SETTLE_MS));
    vTaskDelay(pdMS_TO_TICKS(CH390_RESTART_HOLD_DOWN_MS));

#if DIAG_CH390_RAW_POLL
    ethernetif_diag_ch390_init();
#else
    ethernetif_force_full_recovery(&ipaddr, &netmask, &gateway, cfg->net.mac);
    vendor_id = ethernetif_ch390_get_vendor_id();
    product_id = ethernetif_ch390_get_product_id();
    revision = ethernetif_ch390_get_revision();
    if ((vendor_id != CH390_EXPECTED_VENDOR_ID) || (product_id != CH390_EXPECTED_PRODUCT_ID)) {
        debug_log_printf("[NET] restart-recovery id-warn vid=0x%04X pid=0x%04X rev=0x%02X free=%lu min=%lu\r\n",
                         (unsigned int)vendor_id,
                         (unsigned int)product_id,
                         (unsigned int)revision,
                         (unsigned long)xPortGetFreeHeapSize(),
                         (unsigned long)xPortGetMinimumEverFreeHeapSize());
    }
#endif

    app_clear_network_task_stop();
    g_netif_ready = pdTRUE;
    app_start_network_tasks();
    app_clear_network_restart_request();
    return pdTRUE;
}

void NetPollTask(void *argument)
{
    const device_config_t *cfg;
#if !DIAG_CH390_RAW_POLL
    ip4_addr_t ipaddr;
    ip4_addr_t netmask;
    ip4_addr_t gateway;
#else
    static uint8_t s_diag_rx_buffer[CH390_PKT_MAX];
#endif
    BaseType_t loop_logged = pdFALSE;

    (void)argument;

    debug_log_write("[NET] task-entry\r\n");
    cfg = config_get();
    debug_log_write("[NET] config-ok\r\n");

#if DIAG_CH390_RAW_POLL
    g_netif_phase = 1u;
    debug_log_write("[NET] diag-ch390-init enter\r\n");
    ethernetif_diag_ch390_init();
    g_netif_phase = 7u;
    debug_log_write("[NET] diag-ch390-init exit\r\n");

    if (g_netif_init_ok != 1)
    {
        for (;;)
        {
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }

    g_netif_ready = pdTRUE;
    app_start_network_tasks();
    debug_log_write("[NET] diag-ch390-ready\r\n");
#else
    debug_log_write("[NET] tcpip-init enter\r\n");
    tcpip_init(NULL, NULL);
    debug_log_write("[NET] tcpip-init exit\r\n");

    vTaskDelay(pdMS_TO_TICKS(50));
    debug_log_write("[NET] post-delay\r\n");

    IP4_ADDR(&ipaddr, cfg->net.ip[0], cfg->net.ip[1], cfg->net.ip[2], cfg->net.ip[3]);
    IP4_ADDR(&netmask, cfg->net.mask[0], cfg->net.mask[1], cfg->net.mask[2], cfg->net.mask[3]);
    IP4_ADDR(&gateway, cfg->net.gw[0], cfg->net.gw[1], cfg->net.gw[2], cfg->net.gw[3]);

    g_netif_phase = 1u;
    debug_log_printf("[NET] netif-call hwm=%lu\r\n", (unsigned long)uxTaskGetStackHighWaterMark(NULL));
    debug_log_write("[NET] netif-init enter\r\n");
    lwip_netif_init(&ipaddr, &netmask, &gateway);
    g_netif_phase = 7u;
    debug_log_write("[NET] netif-init exit\r\n");
    debug_log_printf("[NET] post-init ok=%ld hwm=%lu free=%lu min=%lu\r\n",
                     (long)g_netif_init_ok,
                     (unsigned long)uxTaskGetStackHighWaterMark(NULL),
                     (unsigned long)xPortGetFreeHeapSize(),
                     (unsigned long)xPortGetMinimumEverFreeHeapSize());

    if (g_netif_init_ok != 1) {
        for (;;) {
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }

    debug_log_write("[NET] pre-ready\r\n");
    g_netif_ready = pdTRUE;
    debug_log_write("[NET] start-network-tasks call\r\n");
    app_start_network_tasks();
    debug_log_printf("[NET] post-ready free=%lu min=%lu\r\n",
                     (unsigned long)xPortGetFreeHeapSize(),
                     (unsigned long)xPortGetMinimumEverFreeHeapSize());
    debug_log_write("[NET] netif-ready\r\n");
#endif

    for (;;) {
        if (loop_logged == pdFALSE) {
            g_netif_phase = 8u;
            debug_log_write("[NET] loop-enter\r\n");
            loop_logged = pdTRUE;
        }

        if (app_network_restart_requested() != pdFALSE) {
            (void)net_poll_restart_network_stack(cfg);
        }

        (void)xSemaphoreTake(xNetSemaphore, pdMS_TO_TICKS(2));

#if DIAG_CH390_RAW_POLL
        ethernetif_diag_poll_status();

        if (ch390_read_reg(CH390_NSR) & NSR_RXRDY)
        {
            uint8_t rx_status = 0u;
            uint32_t rx_len = ch390_runtime_receive_packet(s_diag_rx_buffer, &rx_status);

            if (rx_len > 0u)
            {
                debug_log_printf("[RAW] rx len=%lu st=0x%02X h=%02X %02X %02X %02X\r\n",
                                 (unsigned long)rx_len,
                                 (unsigned int)rx_status,
                                 (unsigned int)s_diag_rx_buffer[0],
                                 (unsigned int)s_diag_rx_buffer[1],
                                 (unsigned int)s_diag_rx_buffer[2],
                                 (unsigned int)s_diag_rx_buffer[3]);
            }
        }
#else
        if (g_netif_ready != pdFALSE) {
            ethernetif_poll();
            ethernetif_check_link();
        }
#endif
    }
}
