/*
 * 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/udp.h"

/*
 * Definitions.
 */

/*
 * Variables.
 */
uint32_t SystemTick_Count;
struct netif gnetif;
char app_udp_client_data[] = "The client echo.";

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

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

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

/* Receive callback, send the same data to destination address. */
static void app_udp_server_callback(void *arg,struct udp_pcb *upcb,struct pbuf *p,const ip_addr_t *addr, u16_t port)
{
    udp_send(upcb,p);  /* Send data to destination address. */
    pbuf_free(p);
}

/* UDP client send characters to server. */
void app_udp_client_send(struct udp_pcb *upcb, char* data)
{
    struct pbuf *p;
    /* Allocate memory space. */
    p = pbuf_alloc(PBUF_TRANSPORT, strlen((char*)data), PBUF_POOL);
    if (p != NULL)
    {
        pbuf_take(p, (char*)data, strlen((char*)data));
        udp_send(upcb, p);
        pbuf_free(p);
    }
}

void app_udp_client_init(void)
{
    ip_addr_t udp_server_addr;

    struct udp_pcb * pcb;
    IP4_ADDR(&udp_server_addr, BOARD_UDP_SERVER_IPADDR0, BOARD_UDP_SERVER_IPADDR1, BOARD_UDP_SERVER_IPADDR2, BOARD_UDP_SERVER_IPADDR3);
    pcb = udp_new(); /* Generate a new UDP control block */
    if (pcb != NULL)
    {
        udp_bind(pcb, IP_ADDR_ANY, BOARD_UDP_OWN_PORT);
        udp_connect(pcb, &udp_server_addr, BOARD_UDP_ECHO_PORT);
        udp_recv(pcb, app_udp_server_callback, NULL);  /* Receive callback. */
        app_udp_client_send(pcb, app_udp_client_data);
    }
}

/* EOF. */
