#include <windows.h>

#define BUFSIZE 4096

int const clientPort = 1111;
int const viewerPort = 2222;

CRITICAL_SECTION clientDataSection;

SOCKET gclient;
sockaddr_in gclientInfo; // Klient, který se připojil
int gaddrlen;            // Velikost adresy vzdáleného počítače


std::string dataProViewer()
{
	std::string data;
	for(int poce=0;poce<robots.size();poce++)
	{
		if(robots[poce].active)
		{
			data += IntToStr( poce ) + ";";
			data += IntToStr( robots[poce].Rcolor ) + ";";
			data += IntToStr( robots[poce].Gcolor ) + ";";
			data += IntToStr( robots[poce].Bcolor ) + ";";
			data += FloatToStr( robots[poce].positionX ) + ";";
			data += FloatToStr( robots[poce].positionY ) + ";";
			data += FloatToStr( robots[poce].rotation ) + ";";
			data += FloatToStr( robots[poce].leftMotorSpeed ) + ";";
			data += FloatToStr( robots[poce].rightMotorSpeed ) + ";";
			data += IntToStr( robots[poce].leftMotorPower ) + ";";
			data += IntToStr( robots[poce].rightMotorPower ) + ";";
			data += IntToStr( robots[poce].leftEncoderValue ) + ";";
			data += IntToStr( robots[poce].rightEncoderValue ) + ";";
			data += IntToStr( robots[poce].leftServoValue ) + ";";
			data += IntToStr( robots[poce].rightServoValue ) + ";";
			for(int i=0;i<numberOfSensors;i++)
			{
				data += IntToStr( robots[poce].sensorValues[i] ) + ";";
			}
			data += IntToStr( robots[poce].led ) + ";";
			data += IntToStr( robots[poce].startButton ) + ";";
			data += "$";
		}
	}
	Sleep(50);
	return data;
}


SOCKET start(int port)
{
	WORD wVersionRequested = MAKEWORD(1,1); // Číslo verze
	WSADATA data;           // Struktura s info. o knihovně;
	SOCKET mainSocket;      // Soket
	sockaddr_in sockName;   // "Jméno" soketu a číslo portu

	if (WSAStartup(wVersionRequested, &data) != 0)
	{
		std::cerr << "Nepodařilo se inicializovat sokety" << std::endl;
		return -1;
	}
	mainSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (mainSocket == INVALID_SOCKET)
	{
		std::cerr << "Nelze vytvořit soket" << std::endl;
		WSACleanup();
		return -1;
	}
	sockName.sin_family = AF_INET;
	sockName.sin_port = htons(port);
	sockName.sin_addr.s_addr = INADDR_ANY;
	if (bind(mainSocket, (sockaddr *)&sockName, sizeof(sockName)) == SOCKET_ERROR)
	{
		std::cerr << "Problém s pojmenováním soketu." << std::endl;
		WSACleanup();
		return -1;
	}
	if (listen(mainSocket, 10) == SOCKET_ERROR)
	{
		std::cerr << "Problém s vytvořením fronty" << std::endl;
		WSACleanup();
		return -1;
	}
	return mainSocket;
}


std::string zpracujPozadavek(std::string dotaz, int threadId)
{
	std::size_t index = dotaz.find('\n');
	if (index != std::string::npos)
	{
		// TODO
	}

	std::string type(dotaz.begin(), dotaz.begin() + index);
	std::string value(dotaz.begin() + index + 1, dotaz.end());

	//EnterCriticalSection(&IOSection);
	//std::cout << threadId << "\t" << type << std::endl;
	//std::cout << value << std::endl << std::endl;
	//LeaveCriticalSection(&IOSection);
	return prepisHodnoty(threadId, type, value);
}


DWORD WINAPI clientCommunication( LPVOID lpParam )
{	
	std::string text;       // Přijímaný text
	char buf[BUFSIZE];      // Přijímací buffer
	int size;               // Počet přijatých a odeslaných bytů
	int count = 0;          // Počet připojení

	EnterCriticalSection(&clientDataSection);
	int threadId = threads;
	threads++;
	SOCKET client = gclient;
	sockaddr_in clientInfo = gclientInfo; // Klient, který se připojil
	int addrlen = gaddrlen;            // Velikost adresy vzdáleného počítače
	LeaveCriticalSection(&clientDataSection);

	if (client == INVALID_SOCKET)
	{
		std::cerr << "Problém s přijetím spojeni" <<std::endl;
	}
	EnterCriticalSection(&IOSection);
	std::cout << "Někdo se připojil z adresy: " << inet_ntoa((in_addr)clientInfo.sin_addr) << std::endl;
	LeaveCriticalSection(&IOSection);
	int totalSize = 0;

	text = "";
	while(text != "finish\n")
	{
		text = "";
		do
		{
			size = recv(client, buf, BUFSIZE-1, 0);
			totalSize += size;
			text += buf;
		}while(size == -1 && text != "");
		std::string odpoved = zpracujPozadavek(text,threadId);
		//std::cout << "odpoved:  " << odpoved << std::endl << std::endl << std::endl;
		size = send(client, odpoved.c_str(), odpoved.size(), 0);
		if (size == SOCKET_ERROR)
		{
			std::cerr << "Problém s odesláním dat" << std::endl;
			text = "finish";
		}
	}
	std::string odpoved = zpracujPozadavek(text,threadId);
	closesocket(client);
	return 0;
}


DWORD WINAPI viewerCommunication( LPVOID lpParam )
{
	std::string text;       // Přijímaný text
	int size;               // Počet přijatých a odeslaných bytů
	int count = 0;          // Počet připojení

	EnterCriticalSection(&clientDataSection);
	SOCKET client = gclient;
	sockaddr_in clientInfo = gclientInfo; // Klient, který se připojil
	int addrlen = gaddrlen;            // Velikost adresy vzdáleného počítače
	LeaveCriticalSection(&clientDataSection);

	if (client == INVALID_SOCKET)
	{
		std::cerr << "Problém s přijetím spojeni" <<std::endl;
	}
	EnterCriticalSection(&IOSection);
	std::cout << "Někdo se připojil z adresy: " << inet_ntoa((in_addr)clientInfo.sin_addr) << std::endl;
	LeaveCriticalSection(&IOSection);
	int totalSize = 0;

	bool isConnect = true;
	while(isConnect)
	{
		std::string odpoved;
		odpoved = dataProViewer();

		int sent = 0;
		while(sent != odpoved.size())
		{
			size = send(client, odpoved.c_str(), odpoved.size()-sent, 0);
			if (size == SOCKET_ERROR)
			{
				std::cerr << "Problém s odesláním dat" << std::endl;
				text = "finish";
				isConnect = false;
			}
			odpoved.append(odpoved.begin()+size, odpoved.end());
			sent += size;
		}
	}
	closesocket(client);
	return 0;
}


DWORD WINAPI posloucham(int port, DWORD WINAPI zpracovavani( LPVOID lpParam ) )
{
	DWORD   dwThreadId;
	WORD wVersionRequested = MAKEWORD(1,1); // Číslo verze
	SOCKET mainSocket;      // Soket
	std::string text;       // Přijímaný text
	int count = 0;          // Počet připojení

	mainSocket = start(port);

	while(!konec)
	{
		EnterCriticalSection(&IOSection);
		std::cout << "Cekam na nove spojeni..." << std::endl;
		LeaveCriticalSection(&IOSection);
		gaddrlen = sizeof(gclientInfo);
		gclient = accept(mainSocket, (sockaddr*)&gclientInfo, &gaddrlen);
		HANDLE komunikace = CreateThread(NULL,0,zpracovavani,0,0,&dwThreadId);
	}
	EnterCriticalSection(&IOSection);
	std::cout << "Končím" << std::endl;
	LeaveCriticalSection(&IOSection);
	closesocket(mainSocket); 
	WSACleanup();
	return 0;
}


DWORD WINAPI poslouchamClienta( LPVOID lpParam )
{
	return posloucham(clientPort,clientCommunication);
}


DWORD WINAPI poslouchamViewer( LPVOID lpParam )
{
	return posloucham(viewerPort,viewerCommunication);
}
