#ifndef RELEASE_VERSION
#include ".\debugsock.h"

static DWORD WINAPI RunningThreadCall(LPVOID param) {
  DebugSock* pMG = (DebugSock*)param;
  return pMG->RunningThread();
}

DWORD DebugSock::RunningThread() {

	DWORD i;
	DWORD Total;
	INT Ret;
	WSADATA wsaData;
	TotalSockets=0;
	_listenTimeout.tv_sec=SECTIMEOUTLISTEN;
	_listenTimeout.tv_usec=0;

	PFrogConsole::instance()->addconsole(0, "DEBUG PORT %d", _PORT);


	if ((Ret = WSAStartup(MAKEWORD(2,0),&wsaData)) != 0) {
		PFrogConsole::instance()->addconsole(0, "WSAStartup() failed with error %d", Ret);
		WSACleanup();
		return 0;
	}

	// Create a socket.
	if ((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) {
		PFrogConsole::instance()->addconsole(0, "WSASocket() failed with error %d", WSAGetLastError());
		return 0;
	}
	InternetAddr.sin_family = AF_INET;
	//InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	InternetAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	InternetAddr.sin_port = htons(_PORT);

	if (bind(ListenSocket, (SOCKADDR *) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR) {
		PFrogConsole::instance()->addconsole(0, "Binding failed with error %d", WSAGetLastError());
		return 0;
	}

	if (listen(ListenSocket, 5)) {
		PFrogConsole::instance()->addconsole(0, "listen failed with error %d", WSAGetLastError());
		return 0;
	}

	// Change the socket mode on the listening socket from blocking to non-block 
	NonBlock = 0;
	if (ioctlsocket(ListenSocket, FIONBIO, &NonBlock) == SOCKET_ERROR) {
		PFrogConsole::instance()->addconsole(0, "ioctlsocket() failed");
		return 0;
	}

	// esegue il loop del socket
	while (true) {
		// Initialize the Read and Write socket set.
		FD_ZERO(&Reader);
		FD_ZERO(&Writer);

		// Check for connection attempts.
		FD_SET(ListenSocket, &Reader);

		// Set Read and Write notification for each socket based on the
		// current state the buffer.
		for (i = 0; i < TotalSockets; i++) {
			if (SocketList[i]->RecvBytes > SocketList[i]->SendBytes)
				FD_SET(SocketList[i]->Socket, &Writer);
			else
				FD_SET(SocketList[i]->Socket, &Reader);
		}

		// controlla per nuove connessioni solo se non sei già connesso
		if (!this->isConnected()) {
			if ((Total = select(0, &Reader, &Writer, NULL, &_listenTimeout)) == SOCKET_ERROR) {
				PFrogConsole::instance()->addconsole(0,"select function returned with error %d", WSAGetLastError());
				return 0;
			}

			// Check for arriving connections on the listening socket.
			if (FD_ISSET(ListenSocket, &Reader)) {

				Total--;

				if ((AcceptSocket = accept(ListenSocket, NULL, NULL)) != INVALID_SOCKET) {
					// Set the accepted socket to non-blocking mode so the server will
					// not get caught in a blocked condition on WSASends
					NonBlock = 0;

					if (ioctlsocket(AcceptSocket, FIONBIO, &NonBlock) == SOCKET_ERROR) {
						PFrogConsole::instance()->addconsole(0,"ioctlsocket() failed with error %d", WSAGetLastError());
						return 0;
					}

					if (CreateSocketInformation(AcceptSocket) == FALSE)
						return 0;

				} else {		

					if (WSAGetLastError() != WSAEWOULDBLOCK) {
						PFrogConsole::instance()->addconsole(0,"accept() failed with error %d", WSAGetLastError());
						return 0;
					}
				}

			}
		} // maio - controlla per nuove connessioni solo se non sei già connesso
		else {
			Total=1;
		}

		// Check each socket for Read and Write notification for Total number of sockets
		for (i = 0; Total > 0 && i < TotalSockets; i++) {

			LPSOCKET_INFORMATION SocketInfo = SocketList[i];

			// If the Reader is marked for this socket then this means data
			// is available to be read on the socket.

			if (FD_ISSET(SocketInfo->Socket, &Reader)) {

				Total--;

				SocketInfo->DataBuf.buf = SocketInfo->Buffer;
				SocketInfo->DataBuf.len = BUFFERSIZE;
				Flags = 0;

				if (WSARecv(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &RecvBytes, &Flags, NULL, NULL) == SOCKET_ERROR) {

					if (WSAGetLastError() != WSAEWOULDBLOCK) {
						PFrogConsole::instance()->addconsole(0,"Receive failed with error");
						FreeSocketInformation(i);
					}
					continue;
				} else {

					SocketInfo->RecvBytes = RecvBytes;

					this->_OnDataReceive(i, SocketInfo->DataBuf.buf, RecvBytes);

					// If zero bytes are received, this indicates connection is closed.
					if (RecvBytes == 0) {
						FreeSocketInformation(i);
						continue;
					}

				}

			}

			// If the Writer is marked on this socket then this means the internal
			// data buffers are available for more data.
			if (FD_ISSET(SocketInfo->Socket, &Writer)) {

				Total--;

				SocketInfo->DataBuf.buf = SocketInfo->Buffer + SocketInfo->SendBytes;
				SocketInfo->DataBuf.len = SocketInfo->RecvBytes - SocketInfo->SendBytes;

				if (WSASend(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &SendBytes, 0, NULL, NULL) == SOCKET_ERROR) {

					if (WSAGetLastError() != WSAEWOULDBLOCK) {
						PFrogConsole::instance()->addconsole(0, "Send failed with error");
						FreeSocketInformation(i);
					}

					continue;

				} else {

					SocketInfo->SendBytes += SendBytes;

					if (SocketInfo->SendBytes == SocketInfo->RecvBytes) {
						SocketInfo->SendBytes = 0;
						SocketInfo->RecvBytes = 0;
					}

				}

			}

		} // 	for (i = 0; Total > 0 && i < TotalSockets; i++) {

	} // while true

	return 0;


} // RunningThread

DebugSock::DebugSock(int PORT, bool debugMode)
{
	_isConnected=false;
	onsocketreceive=NULL;
	bufferInCount=0;
	_debugMode=debugMode;
	// tronca file se siamo in modalità debug
	if (_debugMode) logfile(true, "");

	_PORT=PORT;
	// crea il thread che processa il socket
	thread = CreateThread(NULL, 0, RunningThreadCall, this, 0, NULL);
	SetThreadPriority(thread, THREAD_PRIORITY_NORMAL);

}

// logga su file
void DebugSock::logfile(bool truncate, char *pattern, ...)
{
    TCHAR pathname[512], lpFileName[512];

	// extract exe path
	GetModuleFileName(NULL, lpFileName, 255);

	// get the last backslash
	for (int i=_tcslen(lpFileName)-1; i>=0; i--) {
		if ( lpFileName[i] == _T('\\') ) {
			_stprintf(pathname, _T("%.*s"), i+1, lpFileName); 
			break;
		}
	}

	// append the filename
	_tcscat(pathname, _T("debug.log"));


	FILE *f;

	if (truncate)
		 f = _tfopen(pathname, _T("w"));
	else
		 f = _tfopen(pathname, _T("a"));
	
	va_list vl;
	va_start(vl, pattern);
	vsprintf(buffer_temp, pattern, vl);
	va_end(vl);

	fprintf(f, "%s\n", buffer_temp);

	fclose(f);

} // logfile

bool DebugSock::isConnected() { return _isConnected; }

DebugSock::~DebugSock(void)
{
	TerminateThread(thread, 0);
}


BOOL DebugSock::CreateSocketInformation(SOCKET s) {
	LPSOCKET_INFORMATION SI;
	PFrogConsole::instance()->addconsole(0, "Accepted socket");
	
	if ((SI = (LPSOCKET_INFORMATION) GlobalAlloc(GPTR, sizeof(SOCKET_INFORMATION))) == NULL) {
		PFrogConsole::instance()->addconsole(0, "GlobalAlloc() failed");
		return FALSE;
	}

	_isConnected=true;
	bufferInCount=0;

	// Prepare SocketInfo structure for use.
	SI->Socket = s;
	SI->SendBytes = 0;
	SI->RecvBytes = 0;
	SocketList[TotalSockets] = SI;
	TotalSockets++;

	return(TRUE);
}



void DebugSock::FreeSocketInformation(DWORD Index) {
	LPSOCKET_INFORMATION SI = SocketList[Index];
	DWORD i;

	closesocket(SI->Socket);
	PFrogConsole::instance()->addconsole(0, "Closing socket");
	GlobalFree(SI);
	
	// Remove from the socket array
	for (i = Index; i < TotalSockets; i++)
	{
		SocketList[i] = SocketList[i + 1];
	}
	TotalSockets--;

	_isConnected=false;
}



void DebugSock::socksend(unsigned int sockIndex ,char *data)
{
	// logga se richiesto
	if (_debugMode) 
		logfile(false, "[SOCK]>> %s", data);

	// invia al socket
	_socksend(sockIndex, data, strlen(data));
}

void DebugSock::_socksend(unsigned int sockIndex ,char *data, int len)
{
	LPSOCKET_INFORMATION SocketInfo = SocketList[sockIndex];
	
	if (sockIndex >= TotalSockets)
		return;
	
	long lLength = send(SocketInfo->Socket,data,len,0);
	if(lLength < len)
	{
		PFrogConsole::instance()->addconsole(0, "SEND ERROR!");
	}

	// invia newline
	const char newline='\n';
	lLength = send(SocketInfo->Socket,&newline,1,0);

}

// setta callback per ricezione dati su socket
void DebugSock::SetSocketReceiveCallBack(void (*onsocketreceive)(char *text))
{
	this->onsocketreceive=onsocketreceive;
}

void DebugSock::_OnDataReceive(int sockIndex, char *data, long buflen)
{
	// aggiungi dati al buffer, finchè non troviamo un newline
	for (long b=0; b < buflen; b++) {
		if (data[b] != '\n') {
			// aggiungi al buffer, finchè c'è spazio
			if (bufferInCount < BUFFERSIZE-1)
				bufferIn[bufferInCount++]=data[b];
		} else {
			// trovata new line, chiama call back e resetta contatore
			bufferIn[bufferInCount-1]='\0';
			bufferInCount=0;
			PFrogConsole::instance()->addconsole(0, "SOCK <- %s", bufferIn);
			// logga se richiesto
			if (_debugMode) 
				logfile(false, "[SOCK]<< %s", bufferIn);
			// richiama call back se settata
			if (onsocketreceive)
				onsocketreceive(bufferIn);
		}
	}

}

bool DebugSock::isInDebugMode() { return _debugMode; }

#endif
