
#include <stdio.h>
#include <Winsock.h>

int socketOpen(SOCKET *s,unsigned short port)
{
	SOCKET sock;
	struct sockaddr addr;
	int rc;
	unsigned long i = 1;

	sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);

	((struct sockaddr_in *)&addr)->sin_family = AF_INET;
	((struct sockaddr_in *)&addr)->sin_addr.s_addr = INADDR_ANY;
	((struct sockaddr_in *)&addr)->sin_port = htons(port);

	// set non-blocking
	rc = ioctlsocket(sock, FIONBIO, &i);

	if (bind(sock, &addr, (int)sizeof(struct sockaddr)) == SOCKET_ERROR)
	{
		return 1;
	}

	*s = sock;

	return 0;
}

int socketRead(SOCKET s,char *buffer,int nbytes)
{
	int len;
	int count;
	struct sockaddr from;
	
	len = sizeof(struct sockaddr_in);

	count = recvfrom(s, buffer, nbytes, 0, &from, &len);

	return count;
}

int socketWrite(SOCKET s,char *buffer,int nbytes,struct sockaddr *address)
{
	int count;

	count = sendto(s, buffer, nbytes, 0, address, (int)sizeof(struct sockaddr_in));

	return count;
}

int stringtoaddr(char *string,struct sockaddr *address)
{
	unsigned long a1, a2, a3, a4;
	unsigned long ipaddress;
	unsigned long port = 0;
	int ret;

	ret = sscanf(string, "%lu.%lu.%lu.%lu:%lu", &a1, &a2, &a3, &a4, &port);

	if(a1 > 255 || a2 > 255 || a3 > 255 || a4 > 255 || port > 65535 || ret < 4)
	{
		/* bad address */
		((struct sockaddr_in *)address)->sin_family = AF_INET;
		((struct sockaddr_in *)address)->sin_addr.s_addr = INADDR_NONE;
		((struct sockaddr_in *)address)->sin_port = 0;

		return 1;
	}

	ipaddress = (a1 << 24) | (a2 << 16) | (a3 << 8) | a4;
	((struct sockaddr_in *)address)->sin_family = AF_INET;
	((struct sockaddr_in *)address)->sin_addr.s_addr = htonl(ipaddress);
	((struct sockaddr_in *)address)->sin_port = htons((unsigned short)port);

	return 0;
}

typedef struct
{
	char *m_pcData;
	int	m_iMaxSize;
	int	m_iCurSize;
	int m_iReadCount;
} sizebuf_t;

void MSG_WriteCreate(sizebuf_t *buf, char *_acBuffer, int _iLength)
{
	buf->m_pcData = _acBuffer;
	buf->m_iCurSize = 0;
	buf->m_iMaxSize = _iLength;
	buf->m_iReadCount = 0;
}

void *SZ_GetSpace (sizebuf_t *buf, int length)
{
	void *data;

	if (buf->m_iCurSize + length > buf->m_iMaxSize)
	{
		return NULL;
	}

	data = buf->m_pcData + buf->m_iCurSize;
	buf->m_iCurSize += length;

	return data;
}

int MSG_Write(sizebuf_t *sb, void *data, int length)
{
	char *buf;

	if ( (buf = SZ_GetSpace(sb,length)) == NULL)
		return 1;

	memcpy (buf,data,length);

	return 0;
}

int MSG_WriteStringLine (sizebuf_t *sb, char *s)
{
	char *buf;

	if (!s)
		return MSG_Write(sb, "\n", 1);
	else
	{
		if ( (buf = SZ_GetSpace(sb,strlen(s))) == NULL)
			return 1;

		memcpy(buf,s,strlen(s));

		MSG_Write(sb, "\n", 1);
		return 0;
	}
}

void MSG_ReadCreate(sizebuf_t *buf, char *_acBuffer, int _iLength)
{
	buf->m_pcData = _acBuffer;
	buf->m_iCurSize = _iLength;
	buf->m_iMaxSize = _iLength;
	buf->m_iReadCount = 0;
}

// returns -1 if no more characters are available
int MSG_ReadChar(sizebuf_t *msg_read)
{
	int	c;

	if (msg_read->m_iReadCount+1 > msg_read->m_iCurSize)
		c = -1;
	else
		c = (signed char)msg_read->m_pcData[msg_read->m_iReadCount];
	msg_read->m_iReadCount++;

	return c;
}

void MSG_ReadStringLine(sizebuf_t *msg_read, char *_szString)
{
	int		l,c;

	l = 0;
	do
	{
		c = MSG_ReadChar (msg_read);
		if (c == -1 || c == '\n' || c == 0)
			break;
		_szString[l] = c;
		l++;
	} while (1);

	_szString[l] = 0;
}

int main(int argc,char *argv[])
{
	WSADATA libmibWSAdata;
	SOCKET s;
	struct sockaddr address;
	char szPassword[100];
	int iTimer;
	int iState;				/* 0: disconnected; 1: connecting; 2: connected; 3: receiving data */
	int iConnectionTime;
	int iStartTime;
	int iKeepAliveTime;
	DWORD dwPrev,dwCur;
	int iTime;
	char buffer[2000];
	int count;
	sizebuf_t sMsg;
	char szString[100];
	char szID[100];
	char szData[100];

	/* first try Winsock 2.0 */
	if(WSAStartup(MAKEWORD(2, 0),&libmibWSAdata) != 0)
	{
		/* Winsock 2.0 failed, so now try 1.1 */
		if(WSAStartup(MAKEWORD(1, 1),&libmibWSAdata) != 0)
		{
			return 1;
		}
	}

	if (socketOpen(&s,20000) != 0)
	{
		return 1;
	}

	stringtoaddr("127.0.0.1:54420",&address);
	strcpy(szPassword,"test");

	iTimer = 0;

	iState = 1;
	iConnectionTime = iTimer - 2000;

	timeBeginPeriod(1);

	dwPrev = timeGetTime();

	do
	{
		do
		{
			dwCur = timeGetTime();
			iTime = dwCur - dwPrev;
		} while (iTime < 5);
		dwPrev = dwCur;

		iTimer += iTime;

		count = socketRead(s,buffer,2000);
		if (count > 0)
		{
			MSG_ReadCreate(&sMsg,buffer,count);
			MSG_ReadStringLine(&sMsg,szString);

			if (!stricmp(szString,"OK"))
			{
				if (iState == 1)
				{
					iState = 2;
					iStartTime = iTimer - 2000;
				}

				printf("OK\n");
			}
			else if (!stricmp(szString,"WRONGPASSWORD"))
			{
				iState = 0;

				printf("WRONGPASSWORD\n");
			}
			else if (!stricmp(szString,"FULL"))
			{
				iState = 0;

				printf("FULL\n");
			}
			else if (!stricmp(szString,"MSG"))
			{
				if (iState == 2)
				{
					iState = 3;
					iKeepAliveTime = iTimer;
				}

				MSG_ReadStringLine(&sMsg,szID);
				printf("MSG: %s",szID);
				while (sMsg.m_iReadCount < sMsg.m_iCurSize)
				{
					MSG_ReadStringLine(&sMsg,szData);
					printf(" %s",szData);
				}
				printf("\n");

				MSG_WriteCreate(&sMsg,buffer,2000);
				MSG_WriteStringLine(&sMsg,"ACK");
				MSG_WriteStringLine(&sMsg,szID);

				socketWrite(s,sMsg.m_pcData,sMsg.m_iCurSize,&address);

				printf("ACK %s\n",szID);
			}
			else if (!stricmp(szString,"DATA"))
			{
				if (iState == 2)
				{
					iState = 3;
					iKeepAliveTime = iTimer;
				}

				printf("DATA");
				do
				{
					MSG_ReadStringLine(&sMsg,szData);
					printf(" %s",szData);
				} while (szData[0]);
				printf("\n");
			}
			else if (!stricmp(szString,"ALIVE"))
			{
				printf("ALIVE\n");
			}
		}

		if (iState == 1)
		{
			if ((iTimer - iConnectionTime) > 2000)
			{
				MSG_WriteCreate(&sMsg,buffer,2000);
				MSG_WriteStringLine(&sMsg,"CONNECT");
				MSG_WriteStringLine(&sMsg,szPassword);

				socketWrite(s,sMsg.m_pcData,sMsg.m_iCurSize,&address);

				printf("CONNECT\n");

				iConnectionTime = iTimer;
			}
		}
		else if (iState == 2)
		{
			if ((iTimer - iStartTime) > 2000)
			{
				MSG_WriteCreate(&sMsg,buffer,2000);
				MSG_WriteStringLine(&sMsg,"START");
				MSG_WriteStringLine(&sMsg,"1");			/* 0 or 1. 1 to receive the track positions, too */

				socketWrite(s,sMsg.m_pcData,sMsg.m_iCurSize,&address);

				printf("START\n");

				iStartTime = iTimer;
			}			
		}
		else if (iState == 3)
		{
			if ((iTimer - iKeepAliveTime) > 15000)
			{
				MSG_WriteCreate(&sMsg,buffer,2000);
				MSG_WriteStringLine(&sMsg,"KEEPALIVE");

				socketWrite(s,sMsg.m_pcData,sMsg.m_iCurSize,&address);

				printf("KEEPALIVE\n");

				iKeepAliveTime = iTimer;
			}
		}

		Sleep(5);
	} while(1);

	timeEndPeriod(1);

	closesocket(s);

	WSACleanup();

	return 0;
}