#include "eth.h"
#include "system.h"
// net interface
struct netif *netif, server_netif;
// parameters var
ip_addr_t localIpAddr, netmask, gw;
unsigned char mac_ethernet_address[] = {0x00, 0x0a, 0x35, 0x00, 0x01, 0x02}; // MAC
// suppose in LAN 192.168.1.1/24

int EthInit()
{
	netif = &server_netif;
	// local configuration
	IP4_ADDR(&localIpAddr, 192, 168, 1, 1);
	IP4_ADDR(&netmask, 255, 255, 255, 0);
	IP4_ADDR(&gw, 192, 168, 1, 1);

	PRINT_IP(localIpAddr, "Local IP :");
	PRINT_IP(netmask, "Netmask :");
	PRINT_IP(gw, "Gateway :");

	lwip_init();
	if (!xemac_add(netif, &localIpAddr, &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

	return 0;
}

/// @brief hook from ip4_input() function
/// @param pbuf received struct pbuf passed to ip_input()
/// @param input_netif struct netif on which the packet has been received
/// @return always 1
int IPv4InputHook(struct pbuf *pbuf, struct netif *input_netif)
{
	struct ip_hdr *ipHeader = (struct ip_hdr *)pbuf->payload;
	err_t err;

	if(!ip4_addr_netcmp(&localIpAddr,&ipHeader->dest,&netmask))
	{
//		LOG_WARNING("Not subnet ip");
//		LOG_BLANK_LINE;
		pbuf_free(pbuf);
		return 1;
	}

//	ip_addr_t remote;
//	IP4_ADDR(&remote, 192, 168, 1, 100);



	// hardware have change the checksum to a valid value, no need to check again
//	uint16_t ipHeaderLen = IPH_HL_BYTES(ipHeader);
//	if ( ipHeaderLen!= 20 || inet_chksum(ipHeader, IP_HLEN) != 0)
//	{
//		LOG_WARNING("IPv4 Checksum failed.");
//		LOG_BLANK_LINE;
//		pbuf_free(pbuf);
//		return 1;
//	}
//	else
//	{
//		LOG_INFO("IPv4 Checksum Pass.");
//	}

	uint16_t totalLen = lwip_htons(IPH_LEN(ipHeader));
	PRINT_IPV4_HEADER(ipHeader->src,ipHeader->dest);

	*(((uint8_t*)&ipHeader->src)+3) = 100;
	*(((uint8_t*)&ipHeader->dest)+3) = 66;

//	xil_printf(INFO_MSG("IP packet len: %d"), totalLen);
//	Push(macSendQueue, pbuf->payload, totalLen);

//	LOG_BLANK_LINE;
	pbuf_free(pbuf);
	return 1;
}

uint8_t EthSend(uint8_t *payload, int data_num)
{
	err_t err;
	// pbuf
	struct pbuf *pbuf_to_be_sent = pbuf_alloc(PBUF_LINK, data_num, PBUF_POOL);
	memcpy(pbuf_to_be_sent->payload, (u8 *)payload, data_num);
	struct ip_hdr *ipHdr = (struct ip_hdr *)payload;
	const ip4_addr_t dest = {
		ipHdr->dest.addr};

	err = etharp_output(netif, pbuf_to_be_sent, &dest);
	//	err = ethernet_output(netif, pbuf_to_be_sent, (struct eth_addr *)(netif->hwaddr), (const struct eth_addr *)&ethbroadcast, ETHTYPE_IP);

	if (err != ERR_OK)
	{
		xil_printf("Error on udp send : %d\r\n", err);
		pbuf_free(pbuf_to_be_sent);
		return -1;
	}

//	LOG_BLANK_LINE;
	pbuf_free(pbuf_to_be_sent); // free pbuf
	return 0;
}
