/*
 * tcp.c
 *
 *  Created on: 09-Oct-2018
 *      Author: VD
 */

#include "wifi.h"
#include "tcp.h"

struct TcpServers tcpServer;
struct TcpServers tcpServer2;

struct TcpClients tcpClient;

SlSockAddrIn_t  sAddr;

int             iSockIDs1;
int             iNewSockIDs1;
int             iSockIDs2;
int             iNewSockIDs2;

int             iSockIDc1;

volatile unsigned long  g_ulPacketCount = TCP_PACKET_COUNT;

int TcpServer(unsigned short usPort)
{
    SlSockAddrIn_t  sAddr;
    SlSockAddrIn_t  sLocalAddr;
    int             iAddrSize;
    int             iStatus;
    long            lNonBlocking = 1;
    int             iTestBufLen;
    char g_cBsdBuf[BUF_SIZE];

    SlFdSet_t ReadFds;
	SlFdSet_t ActiveReadFds;
	SlTimeval_t         timeout;

	timeout.tv_sec = 1;
	timeout.tv_usec = 0;

	do
	{
		SL_FD_ZERO(&ReadFds);

		iTestBufLen  = BUF_SIZE;

		//filling the TCP server socket address
		sLocalAddr.sin_family = SL_AF_INET;
		sLocalAddr.sin_port = sl_Htons((unsigned short)usPort);
		sLocalAddr.sin_addr.s_addr = 0;

		// creating a TCP socket
		iSockIDs1 = sl_Socket(SL_AF_INET,SL_SOCK_STREAM, 0);
		if( iSockIDs1 < 0 )
		{
			// error
			System_printf("\r\ntcp_server_%d : SOCKET_CREATE_ERROR",usPort);System_flush();
			tcpServer.state=TCP_SERVER_CLOSE;
			tcpServer.action=TCP_SERVER_CHANGE;
			break;
		}

		iAddrSize = sizeof(SlSockAddrIn_t);

		// binding the TCP socket to the TCP server address
		iStatus = sl_Bind(iSockIDs1, (SlSockAddr_t *)&sLocalAddr, iAddrSize);
		if( iStatus < 0 )
		{
			// error
			sl_Close(iSockIDs1);
			System_printf("\r\ntcp_server_%d : BIND_ERROR",usPort);System_flush();
			tcpServer.state=TCP_SERVER_CLOSE;
			tcpServer.action=TCP_SERVER_CHANGE;
			break;
		}

		tcpServer.state=TCP_SERVER_OPEN;
		System_printf("\r\ntcp_server_%d : tcp server open on port %d",usPort,usPort);System_flush();

		// putting the socket for listening to the incoming TCP connection
		iStatus = sl_Listen(iSockIDs1, 0);
		if( iStatus < 0 )
		{
			sl_Close(iSockIDs1);
			System_printf("\r\ntcp_server_%d : LISTEN_ERROR",usPort);System_flush();
			tcpServer.state=TCP_SERVER_CLOSE;
			tcpServer.action=TCP_SERVER_CHANGE;
			break;
		}

		// setting socket option to make the socket as non blocking
		iStatus = sl_SetSockOpt(iSockIDs1, SL_SOL_SOCKET, SL_SO_NONBLOCKING,
								&lNonBlocking, sizeof(lNonBlocking));
		if( iStatus < 0 )
		{
			sl_Close(iSockIDs1);
			System_printf("\r\ntcp_server_%d : SOCKET_OPT_ERROR",usPort);System_flush();
			tcpServer.state=TCP_SERVER_CLOSE;
			tcpServer.action=TCP_SERVER_CHANGE;
			break;
		}
		iNewSockIDs1 = SL_EAGAIN;

		// waiting for an incoming TCP connection
		while( (iNewSockIDs1 < 0) && (tcpServer.action!=TCP_SERVER_CHANGE) && (!WifiSta.change) && (tcpServer.state!=TCP_SERVER_CLOSE) && (WifiSta.state))
		{
			// accepts a connection form a TCP client, if there is any
			// otherwise returns SL_EAGAIN
			iNewSockIDs1 = sl_Accept(iSockIDs1, ( struct SlSockAddr_t *)&sAddr,
									(SlSocklen_t*)&iAddrSize);
			if( iNewSockIDs1 == SL_EAGAIN )
			{
				MAP_UtilsDelay(10000);
			}
			else if( iNewSockIDs1 < 0 )
			{
				// error
				sl_Close(iNewSockIDs1);
				sl_Close(iSockIDs1);
				System_printf("\r\ntcp_server_%d : ACCEPT_ERROR",usPort);System_flush();
				tcpServer.state=TCP_SERVER_CLOSE;
				tcpServer.action=TCP_SERVER_CHANGE;
				break;
			}
		}

		SL_FD_SET(iNewSockIDs1, &ReadFds);

		if(iNewSockIDs1 >= 0)
		{
			System_printf("\r\ntcp_server_%d : tcp client connected",usPort);System_flush();
			tcpServer.receiverOn=true;
			do
			{
				ActiveReadFds = ReadFds;
				iStatus = sl_Select(iNewSockIDs1 + 1, &ActiveReadFds, NULL, \
										   NULL, &timeout);
				if (iStatus > 0)
				{
					memset(g_cBsdBuf,0,sizeof(g_cBsdBuf));
					iStatus = sl_Recv(iNewSockIDs1, g_cBsdBuf, iTestBufLen, 0);
					if( iStatus <= 0 )
					{
					  // error
					  sl_Close(iNewSockIDs1);
					  sl_Close(iSockIDs1);
					  System_printf("\r\ntcp_server_%d : tcp client disconnected",usPort);System_flush();
					  break;
					}
					else
					{
						System_printf("\r\ntcp_server_%d : Recieved data : %s",usPort, g_cBsdBuf);System_flush();
						UART_write(uart,g_cBsdBuf,strlen(g_cBsdBuf));
					}
				}
			}
			while((iStatus >= 0) && (tcpServer.action!=TCP_SERVER_CHANGE) && (!WifiSta.change) && (tcpServer.state!=TCP_SERVER_CLOSE) && (WifiSta.state));
			tcpServer.receiverOn=false;

			// close the connected socket after receiving from connected TCP client
			iStatus = sl_Close(iNewSockIDs1);
			// close the listening socket
			iStatus = sl_Close(iSockIDs1);
		}
    }
    while((tcpServer.action!=TCP_SERVER_CHANGE) && (!WifiSta.change) && (tcpServer.state!=TCP_SERVER_CLOSE) && (WifiSta.state));
	System_printf("\r\ntcp_server_%d : tcp server closed on port %d",usPort, usPort);System_flush();
    tcpServer.state=TCP_SERVER_CLOSE;
    if(WifiSta.change)
    {
    	tcpServer.action=TCP_SERVER_REOPEN;
    }
    if(!WifiSta.state)
    {
    	tcpServer.action=TCP_SERVER_REOPEN;
    }
    if(tcpClient.action==TCP_CLIENT_CHANGE)
	{
    	httpServer.type=ROLE_STA;
		httpServer.state=true;
	}

    return SUCCESS;
}

int TcpServer2(unsigned short usPort)
{
    SlSockAddrIn_t  sAddr;
    SlSockAddrIn_t  sLocalAddr;
    int             iAddrSize;
    int             iStatus;
    long            lNonBlocking = 1;
    int             iTestBufLen;
    char g_cBsdBuf[BUF_SIZE];

    SlFdSet_t ReadFds;
	SlFdSet_t ActiveReadFds;
	SlTimeval_t         timeout;

	timeout.tv_sec = 1;
	timeout.tv_usec = 0;

	do
	{
		SL_FD_ZERO(&ReadFds);

		iTestBufLen  = BUF_SIZE;

		//filling the TCP server socket address
		sLocalAddr.sin_family = SL_AF_INET;
		sLocalAddr.sin_port = sl_Htons((unsigned short)usPort);
		sLocalAddr.sin_addr.s_addr = 0;

		// creating a TCP socket
		iSockIDs2 = sl_Socket(SL_AF_INET,SL_SOCK_STREAM, 0);
		if( iSockIDs2 < 0 )
		{
			// error
			System_printf("\r\ntcp_server_%d : SOCKET_CREATE_ERROR",usPort);System_flush();
			tcpServer2.state=TCP_SERVER_CLOSE;
			tcpServer2.action=TCP_SERVER_CHANGE;
			break;
		}

		iAddrSize = sizeof(SlSockAddrIn_t);

		// binding the TCP socket to the TCP server address
		iStatus = sl_Bind(iSockIDs2, (SlSockAddr_t *)&sLocalAddr, iAddrSize);
		if( iStatus < 0 )
		{
			// error
			sl_Close(iSockIDs2);
			System_printf("\r\ntcp_server_%d : BIND_ERROR",usPort);System_flush();
			tcpServer2.state=TCP_SERVER_CLOSE;
			tcpServer2.action=TCP_SERVER_CHANGE;
			break;
		}

		tcpServer2.state=TCP_SERVER_OPEN;
		System_printf("\r\ntcp_server_%d : tcp server open on port %d",usPort,usPort);System_flush();

		// putting the socket for listening to the incoming TCP connection
		iStatus = sl_Listen(iSockIDs2, 0);
		if( iStatus < 0 )
		{
			sl_Close(iSockIDs2);
			System_printf("\r\ntcp_server_%d : LISTEN_ERROR",usPort);System_flush();
			tcpServer2.state=TCP_SERVER_CLOSE;
			tcpServer2.action=TCP_SERVER_CHANGE;
			break;
		}

		// setting socket option to make the socket as non blocking
		iStatus = sl_SetSockOpt(iSockIDs2, SL_SOL_SOCKET, SL_SO_NONBLOCKING,
								&lNonBlocking, sizeof(lNonBlocking));
		if( iStatus < 0 )
		{
			sl_Close(iSockIDs2);
			System_printf("\r\ntcp_server_%d : SOCKET_OPT_ERROR",usPort);System_flush();
			tcpServer2.state=TCP_SERVER_CLOSE;
			tcpServer2.action=TCP_SERVER_CHANGE;
			break;
		}
		iNewSockIDs2 = SL_EAGAIN;

		// waiting for an incoming TCP connection
		while( (iNewSockIDs2 < 0) && (tcpServer2.action!=TCP_SERVER_CHANGE) && (!WifiSta.change) && (tcpServer2.state!=TCP_SERVER_CLOSE) && (WifiSta.state))
		{
			// accepts a connection form a TCP client, if there is any
			// otherwise returns SL_EAGAIN
			iNewSockIDs2 = sl_Accept(iSockIDs2, ( struct SlSockAddr_t *)&sAddr,
									(SlSocklen_t*)&iAddrSize);
			if( iNewSockIDs2 == SL_EAGAIN )
			{
				MAP_UtilsDelay(10000);
			}
			else if( iNewSockIDs2 < 0 )
			{
				// error
				sl_Close(iNewSockIDs2);
				sl_Close(iSockIDs2);
				System_printf("\r\ntcp_server_%d : ACCEPT_ERROR",usPort);System_flush();
				tcpServer2.state=TCP_SERVER_CLOSE;
				tcpServer2.action=TCP_SERVER_CHANGE;
				break;
			}
		}

		SL_FD_SET(iNewSockIDs2, &ReadFds);

		if(iNewSockIDs2 >= 0)
		{
			System_printf("\r\ntcp_server_%d : tcp client connected",usPort);System_flush();
			tcpServer2.receiverOn=true;
			do
			{
				ActiveReadFds = ReadFds;
				iStatus = sl_Select(iNewSockIDs2 + 1, &ActiveReadFds, NULL, \
										   NULL, &timeout);
				if (iStatus > 0)
				{
					memset(g_cBsdBuf,0,sizeof(g_cBsdBuf));
					iStatus = sl_Recv(iNewSockIDs2, g_cBsdBuf, iTestBufLen, 0);
					if( iStatus <= 0 )
					{
					  // error
					  sl_Close(iNewSockIDs2);
					  sl_Close(iSockIDs2);
					  System_printf("\r\ntcp_server_%d : tcp client disconnected",usPort);System_flush();
					  break;
					}
					else
					{
						System_printf("\r\ntcp_server_%d : Recieved data : %s",usPort,g_cBsdBuf);System_flush();
						UART_write(uart,g_cBsdBuf,strlen(g_cBsdBuf));
					}
				}
			}
			while((iStatus >= 0) && (tcpServer2.action!=TCP_SERVER_CHANGE) && (!WifiSta.change) && (tcpServer2.state!=TCP_SERVER_CLOSE) && (WifiSta.state));
			tcpServer2.receiverOn=false;

			// close the connected socket after receiving from connected TCP client
			iStatus = sl_Close(iNewSockIDs2);
			// close the listening socket
			iStatus = sl_Close(iSockIDs2);
		}
    }
    while((tcpServer2.action!=TCP_SERVER_CHANGE) && (!WifiSta.change) && (tcpServer2.state!=TCP_SERVER_CLOSE) && (WifiSta.state));
	System_printf("\r\ntcp_server_%d : tcp server closed on port %d",usPort,usPort);System_flush();
    tcpServer2.state=TCP_SERVER_CLOSE;
    if(WifiSta.change)
    {
    	tcpServer2.action=TCP_SERVER_REOPEN;
    }
    if(!WifiSta.state)
    {
    	tcpServer2.action=TCP_SERVER_REOPEN;
    }
    if(tcpClient.action==TCP_CLIENT_CHANGE)
	{
    	httpServer.type=ROLE_STA;
		httpServer.state=true;
	}

    return SUCCESS;
}

int TcpClient(unsigned short usPort, unsigned long usIp)
{
    int             iAddrSize;
    int             iStatus;
    long            lNonBlocking = 1;
    char g_cBsdBuf[BUF_SIZE];

    SlFdSet_t ReadFds;
	SlFdSet_t ActiveReadFds;
	SlTimeval_t         timeout;
	timeout.tv_sec = 1;
	timeout.tv_usec = 0;

	tcpClient.state=TCP_CLIENT_OPEN;
	while(1)
	{
		iAddrSize = sizeof(SlSockAddrIn_t);

		// creating a TCP socket
		iSockIDc1 = sl_Socket(SL_AF_INET,SL_SOCK_STREAM, 0);
		if( iSockIDc1 >= 0 )
		{
			// connecting to TCP server
			iStatus = sl_Connect(iSockIDc1, ( SlSockAddr_t *)&sAddr, iAddrSize);
			if( iStatus >= 0 )
			{
				SL_FD_ZERO(&ReadFds);
				// setting socket option to make the socket as non blocking
				iStatus = sl_SetSockOpt(iSockIDc1, SL_SOL_SOCKET, SL_SO_NONBLOCKING,
										&lNonBlocking, sizeof(lNonBlocking));
				SL_FD_SET(iSockIDc1, &ReadFds);

				System_printf("\r\ntcp client connected to %s on port %d",tcpClient.ip, usPort);System_flush();
				tcpClient.receiverOn=true;
				do
				{
					ActiveReadFds = ReadFds;
					iStatus = sl_Select(iSockIDc1 + 1, &ActiveReadFds, NULL, \
											   NULL, &timeout);
					if (iStatus > 0)
					{
						memset(g_cBsdBuf,0,sizeof(g_cBsdBuf));
						iStatus = sl_Recv(iSockIDc1, g_cBsdBuf, BUF_SIZE, 0);
						if( iStatus <= 0 )
						{
						  // error
						  sl_Close(iSockIDc1);
						  break;
						}
						else
						{
							System_printf("\r\nRecieved data : %s",g_cBsdBuf);System_flush();
							UART_write(uart,g_cBsdBuf,strlen(g_cBsdBuf));
						}
					}
				}
				while((iStatus >= 0) && (tcpClient.action!=TCP_CLIENT_CHANGE) && (!WifiSta.change) && (tcpClient.state!=TCP_CLIENT_CLOSE) && (WifiSta.state));
				tcpClient.receiverOn=false;
				iStatus = sl_Close(iSockIDc1);
				System_printf("\r\ntcp client disconnected from %s on port %d",tcpClient.ip, usPort);System_flush();
			}
			else
			{
				// error
				sl_Close(iSockIDc1);
				System_printf("\r\nCONNECT_ERROR",iStatus);System_flush();
			}
		}
		else
		{
			System_printf("\r\nSOCKET_CREATE_ERROR");System_flush();
		}

		if((tcpClient.action==TCP_CLIENT_CHANGE) || (WifiSta.change) || (tcpClient.state==TCP_CLIENT_CLOSE) || (!WifiSta.state))
		{
			break;
		}
	}
    tcpClient.state=TCP_CLIENT_CLOSE;
    if(WifiSta.change)
	{
    	tcpClient.action=TCP_CLIENT_REOPEN;
	}
    if(!WifiSta.state)
	{
    	tcpClient.action=TCP_CLIENT_REOPEN;
	}
    if(tcpServer.action==TCP_SERVER_CHANGE)
	{
		httpServer.type=ROLE_STA;
		httpServer.state=true;
	}

    return SUCCESS;
}

unsigned long IpAddressParser(char *ucCMD)
{
    volatile int i=0;
    unsigned int uiUserInputData;
    unsigned long ulUserIpAddress = 0;
    char *ucInpString;
    ucInpString = strtok(ucCMD, ".");
    uiUserInputData = (int)strtoul(ucInpString,0,10);
    while(i<4)
    {
        //
       // Check Whether IP is valid
       //
       if((ucInpString != NULL) && (uiUserInputData < 256))
       {
           ulUserIpAddress |= uiUserInputData;
           if(i < 3)
               ulUserIpAddress = ulUserIpAddress << 8;
           ucInpString=strtok(NULL,".");
           uiUserInputData = (int)strtoul(ucInpString,0,10);
           i++;
       }
       else
       {
           return 0;
       }
    }

    return ulUserIpAddress;;
}
