#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <errno.h>

#include "raw_sock.h"
#include "protocol.h"
#include "protocol_udp.h"

/****************************************************************************
 * Function   : 	udprecv_sock_init
 * Description: 	init udp server socket

 * Input      : 	N/A
 * Output     : 	N/A
 * Return     :		ERROR for error, serv_sock if no error
****************************************************************************/
int udprecv_sock_init()
{
	int bbroadcast = 1;
	int ret = 0;
	int serv_sock = 0;
	struct sockaddr_in sin_recv = {0};

	sin_recv.sin_family      = AF_INET;
	sin_recv.sin_addr.s_addr = htonl(INADDR_ANY);
	sin_recv.sin_port        = htons(DEFAULT_PROTOCOL_PORT);

	serv_sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (0 > serv_sock)
	{
		DPRINT_ERR("Socket create error.\n");
		return ERROR;
	}

	int opt = 1;
	ret = setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	if (ret < 0)
	{
		DPRINT_ERR("set socket SO_REUSEADDR error: %s\n", strerror(errno));
		close(serv_sock);
		return ERROR;
	}

	ret = setsockopt(serv_sock, SOL_SOCKET, SO_BROADCAST, (char *)&bbroadcast, sizeof(int));
	if (0 > ret)
	{
		DPRINT_ERR("Setopt error for broadcast.\n");
		close(serv_sock);
		return ERROR;
	}

	ret = bind(serv_sock, (struct sockaddr *)&sin_recv, sizeof(sin_recv));
	if (0 != ret)
	{
		close(serv_sock);
		DPRINT_ERR("Socket bind error.\n");
		return ERROR;
	}

	return serv_sock;
}

/****************************************************************************
 * Function   : 	udpsend_sock_init
 * Description: 	init udp send socket

 * Input      : 	N/A
 * Output     : 	N/A
 * Return     :		ERROR for error, send_sock if no error
****************************************************************************/
int udpsend_sock_init(void)
{
	int bbroadcast = 1;
	int ret = 0;
	int send_sock = 0;
	/* static for the global sockaddr pointer */
	static struct sockaddr_in sin = {0};

	sin.sin_family      = AF_INET;
	sin.sin_addr.s_addr = inet_addr(BCAST_IPADDR);
	sin.sin_port        = htons(DEFAULT_PROTOCOL_PORT);

	send_sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (0 > send_sock)
	{
		DPRINT_ERR("Socket create error.\n");
		return ERROR;
	}

	int opt = 1;
	ret = setsockopt(send_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	if (ret < 0)
	{
		DPRINT_ERR("set socket SO_REUSEADDR error: %s\n", strerror(errno));
		close(send_sock);
		return ERROR;
	}

	ret = setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (char *)&bbroadcast, sizeof(int));
	if (0 > ret)
	{
		DPRINT_ERR("Setopt error for broadcast.\n");
		close(send_sock);
		return ERROR;
	}

	g_udp_send.sock_addr= (struct sockaddr *)&sin;

	return send_sock;
}

/****************************************************************************
 * Function   : 	protocol_udp_handler
 * Description: 	handle the udp datagram

 * Input      : 	sock: socket
 					ph: protocol header content
 					sock_addr: socket address
 * Output     : 	N/A
 * Return     :		ERROR for error, OK if no error
****************************************************************************/
int protocol_udp_handler(PROTOCOL_HDR *ph, struct sockaddr *sock_addr)
{
	int ret = 0;

	if ((NULL == ph) || (NULL == sock_addr))
	{
		return ERROR;
	}

	ret = check_hdr_validity(ph,NETWORK_LAYER);
	if (OK != ret)
	{
		DPRINT_ERR("check validity fail!\n");
		return ERROR;
	}

	if (ph->type == TYPE_DISCOVERY)
	{
		//ret = parse_discovery_frame(ph);
		ret = TRUE;
		if (TRUE == ret)
		{
			DPRINT_DBG("send advertisement frame\n");
			send_advertisement_udp(sock_addr);
		}
	}

	return OK;
}


/****************************************************************************
 * Function   : 	send_nvr_advertisement_udp
 * Description: 	broadcast nvr advertisement udp datagram

 * Input      : 	sock_addr: socket address
 * Output     : 	N/A
 * Return     :		ERROR for error, ret of sendto if no error
****************************************************************************/
int send_advertisement_udp(struct sockaddr *sock_addr)
{
	int udpsock = 0;
	RAW_DATA rawdata = {0};
	int ret= ERROR;

	if (sock_addr == NULL)
	{
		DPRINT_ERR("sock_addr error\n");
		return ERROR;
	}

	if (advertisement_content_update() == TRUE)
	{
		advertisement_buf_update(NETWORK_LAYER);
	}
	udpsock = g_udp_send.sock;
	if (0 >= udpsock)
	{
		DPRINT_ERR("udp socket init error\n");
		return ERROR;
	}

	DATA_LOCK(g_adv_frame);
	rawdata.len = g_adv_frame.data_len - sizeof(ETH_HEADER);
	DATA_UNLOCK(g_adv_frame);
	rawdata.data = (void *)(g_adv_buf + sizeof(ETH_HEADER));

	ret = sendto(udpsock, rawdata.data, rawdata.len, 0, sock_addr, sizeof(struct sockaddr));
	if (ret < 0)
	{
		DPRINT_ERR("raw data send error ret = %d\n", ret);
	}

	return ret;
}

