/*
 * Copyright 2021 MindMotion Microelectronics Co., Ltd.
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include "lwip/init.h"
#include "board_init.h"
#include "ethernetif.h"
#include "netif/ethernet.h"
#include "lwip/def.h"
#include "lwip/stats.h"
#include "lwip/etharp.h"
#include "lwip/ip.h"
#include "lwip/timeouts.h"
#include "lwip/tcp.h"

/*
 * Definitions.
 */
char clientstring[] = "New client connection.\r\n";

/*
 * Variables.
 */
uint32_t SystemTick_Count;
struct netif gnetif;

/*
 * Declerations.
 */
void app_lwip_init(void);
void app_tcp_client_init(void);

/*
 * Functions.
 */
int main(void)
{
    SystemTick_Count = 0u;
    BOARD_Init();

    app_lwip_init();
    app_tcp_client_init();

    while (1)
    {
        sys_check_timeouts();
    }
}

/* Returns the current time in milliseconds, this API from lwip/sys.h */
u32_t sys_now(void)
{
    return SystemTick_Count;
}

/* Systick handler. */
void SysTick_Handler(void)
{
    SystemTick_Count += 10u;
}

/* Initialization lwip. */
void app_lwip_init(void)
{
    ip4_addr_t ipaddr;
    ip4_addr_t netmask;
    ip4_addr_t gw;
    IP4_ADDR(&ipaddr, BOARD_IP_ADDR0, BOARD_IP_ADDR1, BOARD_IP_ADDR2, BOARD_IP_ADDR3);
    IP4_ADDR(&netmask, BOARD_NETMASK_ADDR0, BOARD_NETMASK_ADDR1, BOARD_NETMASK_ADDR2, BOARD_NETMASK_ADDR3);
    IP4_ADDR(&gw, BOARD_GW_ADDR0, BOARD_GW_ADDR1, BOARD_GW_ADDR2, BOARD_GW_ADDR3);

    lwip_init();

    netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &ethernet_input);

    netif_set_default(&gnetif);

    if (netif_is_link_up(&gnetif))
    {
        netif_set_up(&gnetif);
    }
    else
    {
        netif_set_down(&gnetif);
    }
}

/* ENET IRQHandler. */
void ENET_IRQHandler()
{
    if (0u != (ENET_GetDmaStatus(BOARD_DEBUG_ENET_PORT) & ENET_INT_TX_DONE) )
    {
        ENET_ClearDmaStatus(BOARD_DEBUG_ENET_PORT, ENET_INT_TX_DONE);
    }
    if (0u != (ENET_GetDmaStatus(BOARD_DEBUG_ENET_PORT) & ENET_INT_RX_DONE) )
    {
        ethernetif_input(&gnetif);
        ENET_ClearDmaStatus(BOARD_DEBUG_ENET_PORT, ENET_INT_RX_DONE);
    }
}

/* Data processing callback function after TCP client receive data */
static err_t app_tcp_client_xfer(void *arg, struct tcp_pcb *pcb, struct pbuf *tcp_recv_pbuf, err_t err)
{
    if (tcp_recv_pbuf != NULL)
    {
        /* Update the receiving window */
        tcp_recved(pcb, tcp_recv_pbuf->tot_len);
        tcp_write(pcb, tcp_recv_pbuf->payload, tcp_recv_pbuf->len, 1u);
        
        pbuf_free(tcp_recv_pbuf);
    }
    else if (err == ERR_OK)
    {
        tcp_close(pcb);
        app_tcp_client_init();
        return ERR_OK;
    }
    return ERR_OK;
}

/* Connection complete callback function */
static err_t app_tcp_client_connected(void *arg, struct tcp_pcb *pcb, err_t err)
{
    /* Send a greeting string to establish a connection */
    tcp_write(pcb, clientstring, strlen(clientstring), 1u);
    /* Configure the receive callback function */
    tcp_recv(pcb, app_tcp_client_xfer);
    
    return ERR_OK;
}

/* Initialization again when tcp client connect error. */
static void app_tcp_client_connecterror(void *arg, err_t err)
{
    /* Restart the connection */
    app_tcp_client_init();
}

/* TCP client initialization. */
void app_tcp_client_init(void)
{
    struct tcp_pcb *tcp_client_pcb;
    ip_addr_t app_server_ip;
    /* Write the IP of the target server into a structure, which is the local connection IP address of the pc. */
    IP4_ADDR(&app_server_ip, BOARD_TCP_SERVER_IPADDR0, BOARD_TCP_SERVER_IPADDR1, BOARD_TCP_SERVER_IPADDR2, BOARD_TCP_SERVER_IPADDR3);
    /* Assign a structure to the TCP client */
    tcp_client_pcb = tcp_new();

    if (tcp_client_pcb != NULL)
    {
        /* Connect with the target server, and the parameters include the target port and the target IP. */
        tcp_connect(tcp_client_pcb, &app_server_ip, BOARD_TCP_SERVER_PORT, app_tcp_client_connected);
        /* Registered connection error handling callback function. */
        tcp_err(tcp_client_pcb, app_tcp_client_connecterror);
    }
}

/* EOF. */
