#include "lwip/err.h"
#include "lwip/udp.h"
#include "lwip/init.h"
#include "lwipopts.h"
#include "lwip/err.h"
#include "lwipopts.h"
#include "netif/xadapter.h"
#include "xil_printf.h"
#include "sleep.h"
#include "xscugic.h"


// net interface
struct netif *netif, server_netif;
// udp protocol control block
struct udp_pcb *pcb;
// parameters var
ip_addr_t ipaddr, netmask, gw;
unsigned char mac_ethernet_address[] = {0x00, 0x0a, 0x35, 0x00, 0x01, 0x02}; // MAC
// suppose in LAN 192.168.1.1/24
// for single udp socket, port 8090<-->8090

uint32_t local_ip = 66;
uint32_t remote_ip = 106;
unsigned local_port = 8090;
unsigned remote_port = 8090;
XScugic gic;
void UdpSendData(uint8_t *send_buff, int data_num);
int InitIntrSystem(XScuGic *gicPtr, u16 deviceId);
/// @brief UDP init
/// @return 0 for success.
int main()
{

	InitIntrSystem(&gic,0);



	netif = &server_netif;
	// local configuration
	IP4_ADDR(&ipaddr, 192, 168, 1, local_ip);
	IP4_ADDR(&netmask, 255, 255, 255, 0);
	IP4_ADDR(&gw, 192, 168, 1, 1);

	lwip_init();
	if (!xemac_add(netif, &ipaddr, &netmask, &gw, mac_ethernet_address, XPAR_XEMACPS_0_BASEADDR))
	{
		xil_printf("Error adding N/W interface\r\n");
		return -1;
	} // add port
	netif_set_default(netif);
	netif_set_up(netif); // set up network

	err_t err;

	// udp control
	pcb = udp_new();
	if (!pcb)
	{
		xil_printf("Error Creating PCB.\r\n");
		return -1;
	}
	// local port
	err = udp_bind(pcb, IP_ADDR_ANY, local_port);
	if (err != ERR_OK)
	{
		xil_printf("Unable to bind to port %d\r\n", local_port);
		return -1;
	}

	uint8_t * data = malloc(1024);

	while(1)
	{
		usleep(10000);
		UdpSendData(data,1024);
//		xemacif_input(netif);

	}

	return 0;
}

/// @brief UDP send data
/// @param send_buff
/// @param data_num is the length in Byte of the send_buff, also is the protocol payload length
void UdpSendData(uint8_t *send_buff, int data_num)
{
	err_t err;

	// pbuf
	struct pbuf *pbuf_to_be_sent = NULL;
	pbuf_to_be_sent = pbuf_alloc(PBUF_TRANSPORT, data_num, PBUF_POOL);
	memcpy(pbuf_to_be_sent->payload, (u8 *)send_buff, data_num);

	// send
	ip_addr_t addr_to_be_sent;
	IP4_ADDR(&addr_to_be_sent, 192, 168, 1, remote_ip);
	err = udp_sendto(pcb, pbuf_to_be_sent, &addr_to_be_sent, remote_port);
	if (err != ERR_OK)
	{
		xil_printf("Error on udp send : %d\r\n", err);
		pbuf_free(pbuf_to_be_sent);
		return;
	}
	pbuf_free(pbuf_to_be_sent); // free pbuf
}



int InitIntrSystem(XScuGic *gicPtr, u16 deviceId)
{
    int status;

    // Init gic
    XScuGic_Config *gicCfgPtr = XScuGic_LookupConfig(deviceId);
    status = XScuGic_CfgInitialize(gicPtr, gicCfgPtr, gicCfgPtr->CpuBaseAddress);
    CHECK_STATUS(status, ERROR_MSG("Init ScuGic Fails"));

    // Init Exception
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                                 (Xil_ExceptionHandler)XScuGic_InterruptHandler,
                                 (void *)gicPtr);
    Xil_ExceptionEnable();

    return XST_SUCCESS;
}
