#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, 66);
	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;
	// filter the ipv4 incoming
	//	if (!ip4_addr_cmp(&(ipHeader->dest), &localIpAddr) &&
	//		!ip4_addr_cmp(&(ipHeader->dest), &tcpServerIp) &&
	//		!ip4_addr_cmp(&(ipHeader->dest), &tcpClientIp))
	//	{
	//		pbuf_free(pbuf);
	//		return 1;
	//	}
	PRINT_IP(ipHeader->src, "Src IP");
	PRINT_IP(ipHeader->dest, "Dst IP");
	uint16_t totalLen = lwip_htons(IPH_LEN(ipHeader));
	xil_printf(INFO_MSG("IP packet len: %d"), totalLen);

	uint16_t ipHeaderLen = IPH_HL_BYTES(ipHeader);
	if ( ipHeaderLen!= 20 || inet_chksum(ipHeader, IP_HLEN) != 0)
	{
		LOG_WARNING("IPv4 Checksum failed.");
		pbuf_free(pbuf);
		return 1;
	}
	else
	{
		LOG_INFO("IPv4 Checksum Pass.");
	}
	//	switch (ipHeader->_proto)
	//	{
	//	case IP_PROTO_UDP:
	//		LOG_INFO("recv UDP");
	//		break;
	//	case IP_PROTO_TCP:
	//
	//		LOG_INFO("recv TCP");
	//		struct tcp_hdr *tcpHeader = (struct tcp_hdr *)((uint8_t *)ipHeader + IP_HLEN);
	//
	//		// client to server
	//		if (ipHeader->src.addr == tcpClientIp.addr)
	//		{
	//			ipHeader->src.addr = localIpAddr.addr;
	//			ipHeader->dest.addr = tcpServerIp.addr;
	//			err = EthSend(pbuf->payload, pbuf->len);
	//		}
	//		// server to client
	//		if (ipHeader->src.addr == tcpServerIp.addr)
	//		{
	//			ipHeader->src.addr = localIpAddr.addr;
	//			ipHeader->dest.addr = tcpClientIp.addr;
	//			err = EthSend(pbuf->payload, pbuf->len);
	//		}
	//		break;
	//	default:
	//		LOG_WARNING("Other Protocol");
	//		break;
	//	}

	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;
}
