/*
 * 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.
 */

/*
 * Variables.
 */
uint32_t SystemTick_Count;
struct netif gnetif;
char echostring[] = "The client connect echo is:\r\n";

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

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

    app_lwip_init();
    app_tcp_server_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);
    }
}

/* TCP server receive processing. */
static err_t app_tcp_server_receive(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,echostring, strlen(echostring), 1u);
        /* send the receive data. */
        tcp_write(pcb, tcp_recv_pbuf->payload, tcp_recv_pbuf->len, 1u);
        pbuf_free(tcp_recv_pbuf);
        tcp_close(pcb);  /* close connect. */
    }
    else if(err == ERR_OK)
    {
        return tcp_close(pcb);
    }
    return ERR_OK;
}


/* TCP server receives the callback function, and this function is called after the client establishes a connection */
static err_t app_tcp_server_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
    tcp_recv(pcb, app_tcp_server_receive);
    return ERR_OK;
}

/* initialization tcp server. */
void app_tcp_server_init(void)
{
    struct tcp_pcb *pcb;
    /* Create new control block. */
    pcb = tcp_new();
    /* Binding local port and IP address. */
    tcp_bind(pcb, IP_ADDR_ANY, BOARD_TCP_SERVER_PORT); /* (TCP_SERVER_PORT)Port number connected to tcp server */
    /* Start monitoring. */
    pcb = tcp_listen(pcb);
    /* Initialize the structure to receive the callback function. */
    tcp_accept(pcb, app_tcp_server_accept);
}

/* EOF. */
