#include <iostream>
#include <winsock2.h>
#include <stdexcept>
#include <string>
#include <process.h>
#include "data.h"
#include "analysis.h"
#include "email.h"

CMutex g_mutex;
int    g_mutexLocks;

SOCKET Init()
{
	unsigned short wVersionRequested;
	WSADATA wsaData;
	int err;
	// WARNING :use a later version require a change in the multicast code with 
	//			a WS2's specific one
	//			not compatible with other platforms
	wVersionRequested = MAKEWORD(1, 1);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		throw std::runtime_error("Unable to start winsock"); 
	}

	if (LOBYTE(wsaData.wVersion) != 1 ||
		HIBYTE(wsaData.wVersion) != 1)
	{
		WSACleanup();
		throw std::runtime_error("Unable to get requested winsock version"); 
	}

	SOCKET sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
	if (sock == INVALID_SOCKET)
		throw std::runtime_error("Failed creating socket");

	try
	{
		sockaddr_in saddr;
		memset( &saddr, 0, sizeof(saddr) );

		saddr.sin_family = AF_INET;
		saddr.sin_port = htons(0xdbdb);
		saddr.sin_addr.S_un.S_addr = htonl(0);

		if (0 != bind( sock, (sockaddr*)&saddr, sizeof(saddr) ))
			throw std::runtime_error("Unable to bind socket");

		if (0 != listen( sock, 5 ))
			throw std::runtime_error("Unable to listen on socket");
	}
	catch (...)
	{
		closesocket( sock );
		throw;
	}

	return sock;
}

struct ServiceThreadData
{
	SOCKET sock;
	sockaddr_in sender;
};

void ServiceThread( void * ptr )
{
	std::auto_ptr<ServiceThreadData> pData( static_cast<ServiceThreadData *>(ptr) );
	Socket sock( pData->sock );

	try
	{
		SSessionHeader hdr;
		ReadSock( sock, hdr );
		{
			SCOPED_GLOBAL_LOCK;
			std::cout << "Reading " << hdr.numPackets << " packets" << std::endl;
		}
		for (uint32 i=0; i<hdr.numPackets; i++)
		{
			SPacketPtr pPkt = new SPacket();
			ReadSock( sock, *pPkt );
			SCOPED_GLOBAL_LOCK;
			pPkt->session = hdr.id;
			pPkt->senderAddress = *(ULONG*)&pData->sender.sin_addr;

			AddPacket( pPkt );
		}
		SCOPED_GLOBAL_LOCK;
		std::cout << "Read completed successfully" << std::endl;
	}
	catch (std::exception& e)
	{
		SCOPED_GLOBAL_LOCK;
		std::cout << "Error servicing client: " << e.what() << std::endl;
	}
	catch (...)
	{
		SCOPED_GLOBAL_LOCK;
		std::cout << "Unexpected error occured servicing client" << std::endl;
	}
}

void MainLoop( SOCKET sock )
{
	while (true)
	{
		sockaddr_in caddr;
		int size = sizeof(caddr);
		SOCKET child = accept( sock, (sockaddr*)&caddr, &size );

		ServiceThreadData * pData = new ServiceThreadData;
		pData->sock = child;
		pData->sender = caddr;
		_beginthread( ServiceThread, 0, pData );
	}
}

int main(void)
{
	int r = 0;

	std::cout << "Starting Stream Analysis Server" << std::endl;

	SOCKET sock = INVALID_SOCKET;
	try
	{
		sock = Init();
		_beginthread( GarbageCollector, 0, NULL );
		_beginthread( Analyser, 0, NULL );
		MainLoop(sock);
	}
	catch (std::exception& e)
	{
		SCOPED_GLOBAL_LOCK;
		std::cout << "Error: " << e.what() << std::endl;
		r = 1;
	}
	catch (...)
	{
		SCOPED_GLOBAL_LOCK;
		std::cout << "Unexpected error occured" << std::endl;
		r = 2;
	}

	SCOPED_GLOBAL_LOCK;
	std::cout << "Server Exiting" << std::endl;
	if (sock != INVALID_SOCKET)
		closesocket(sock);

	return r;
}
