#include "StdAfx.h"
#include "JniorConnection.h"
#include "Jnior.h"

#include "Constants.h"
#include "Utility.h"
#include "CRC16.h"

#include <time.h>


DWORD64 GetMilliTime(void);

CJniorConnection::CJniorConnection(CJnior* jnior)
{
	m_jnior = jnior;

	m_quit = false;

	m_nextReadPos = 0;
	m_pos = 0;
	m_recvState = 0;

	m_dumpFileName = NULL; //tosstring(jnior->m_handle);
}

CJniorConnection::~CJniorConnection(void)
{
	m_jnior->RemoveConnection(this);
}


int CJniorConnection::SetSocket(SOCKET sckt) {
	// assign the socket
	m_sckt = sckt;

	//// we are connected
	//this->m_connectionStatus = CJnior::STATUS_CONNECTED;

	//set receive timeout
	if (setsockopt(m_sckt
		, SOL_SOCKET
		, SO_RCVTIMEO
		, (const char *)&SEND_RECV_TIMEOUT
		, sizeof(SEND_RECV_TIMEOUT)) != 0)
	{
		//if (!m_cancel) {
		//	return Error(STATUS_CONNECTION_FAILED);
		//} else {
		//	return Error(STATUS_CANCELLED);
		//}
	}

	//set send timeout
	if (setsockopt(m_sckt
		, SOL_SOCKET
		, SO_SNDTIMEO
		, (const char *)&SEND_RECV_TIMEOUT
		, sizeof(SEND_RECV_TIMEOUT)) != 0)
	{
		//if (!m_cancel) {
		//	return Error(STATUS_CONNECTION_FAILED);
		//} else {
		//	return Error(STATUS_CANCELLED);
		//}
	}


	m_thd = new ReceiverThread(this);

	////if we havent created a thread to handle incoming bytes, do so now
	//if (m_hThread == NULL) {
	//	m_quit = false;
	//	m_hThread = CreateThread(
	//		NULL,										// default security attributes 
	//		0,											// use default stack size  
	//		(LPTHREAD_START_ROUTINE) this->ReceiverThd,	// thread function 
	//		this,										// argument to thread function 
	//		0,											// use default creation flags 
	//		&m_dwThreadId);								// returns the thread identifier 
	//}

	//m_shouldReconnect = true;

	//Status(STATUS_ACCEPTED_CONNECTION);

	//return Status(STATUS_CONNECTED);

	// really we shouldnt be sending this here since the JNIOR only sends it once 
	// a login is successfull but jerrid hadnt implemented the login in the version 
	// of the ATO that i have.  But we better sleep for a second to give the JNIOR Dll 
	// time to start the receiver thread
	Sleep(1000);
	BuildMonitorPacket();
	BuildExtendedMonitorPacket();

	return 0;
}

int CJniorConnection::Disconnect() {
	m_thd->m_quit = true;
	closesocket(m_sckt);
	return STATUS_OK;
}

void CJniorConnection::RemoveConnection() {
	m_jnior->RemoveConnection(this);
}

int CJniorConnection::BuildLoginPacket(int level) {
	char* bytes = new char[0xff];
	int m_pos = 0;

	bytes[m_pos++] = 125;
	bytes[m_pos++] = level;

	if (Send(bytes, m_pos) == STATUS_FAILED)
		return STATUS_FAILED;

	delete bytes;

	return STATUS_OK;
}

int CJniorConnection::BuildMonitorPacket() {
	if (m_quit == true) return -1;

	char* bytes = new char[0xff];
	int m_pos = 0;

	bytes[m_pos++] = MONITOR_PACKET;

	char* version = "jr310 3.4.1113.1200\0";

	int versionSize = strlen(version);
	bytes[m_pos++] = versionSize;
	strncpy((char*)&bytes[m_pos], version, versionSize);
	m_pos += versionSize;

	for (int i = 0; i < 8; i++) {
		bytes[m_pos++] = (m_jnior->GetInputs() >> i) & 0x01;
		bytes[m_pos++] = 0;
		int counter = m_jnior->GetInputCounter(i);
		setint(bytes, m_pos, counter);
		bytes[m_pos++] = 0;
		bytes[m_pos++] = 0;
	}

	for (int i = 0; i < 8; i++) {
		bytes[m_pos++] = (m_jnior->GetOutputs() >> i) & 0x01;
	}

	DWORD64 millis = GetMilliTime();
	setlong(bytes, m_pos, millis);

	int result = Send(bytes, m_pos);

	delete bytes;

	return result;
}

/**
 * Added this 02-22-2017 to build the exteded moniotr packet for the 414 and 412.
**/
int CJniorConnection::BuildExtendedMonitorPacket() {
	if (m_quit == true) return -1;

	int result = 0;
	int inputCount = m_jnior->GetInputCount();
	int outputCount = m_jnior->GetOutputCount();

	char* bytes = new char[0xff];
	int m_pos = 0;

	bytes[m_pos++] = EXTENDED_MONITOR_PACKET;

	bytes[m_pos++] = inputCount - 8;
	if (8 < inputCount) {
		for (int i = 8; i < inputCount; i++) {
			bytes[m_pos++] = (m_jnior->GetInputs() >> i) & 0x01;
			bytes[m_pos++] = 0;
			int counter = m_jnior->GetInputCounter(i);
			setint(bytes, m_pos, counter);
			bytes[m_pos++] = 0;
			bytes[m_pos++] = 0;
		}
	}

	bytes[m_pos++] = outputCount - 8;
	if (8 < outputCount) {
		for (int i = 8; i < outputCount; i++) {
			bytes[m_pos++] = (m_jnior->GetOutputs() >> i) & 0x01;
		}
	}

	DWORD64 millis = GetMilliTime();
	setlong(bytes, m_pos, millis);

	result = Send(bytes, m_pos);

	delete bytes;

	return result;
}

void CJniorConnection::hexdump(void *pAddressIn, int offset, int size, char direction)
{
	char szBuf[100];
	long lIndent = 12;
	long lOutLen, lIndex, lIndex2, lOutLen2;
	long lRelPos;
	struct { char *pData; unsigned long lSize; } buf;
	unsigned char *pTmp, ucTmp;
	unsigned char *pAddress = (unsigned char *)pAddressIn;

	buf.pData = (char *)pAddress + offset;
	buf.lSize = size;

	//mutex.MutexOn();

	  //FILE* file = fopen(dumpFileName, "a");
//	  char timeStr [9];
//			_strtime( timeStr );
   //   fprintf(file, "%s\n", timeStr);
	  //fclose(file);
//	Log(timeStr);

	while (buf.lSize > 0)
	{
		pTmp = (unsigned char *)buf.pData;
		lOutLen = (int)buf.lSize;
		if (lOutLen > 16)
			lOutLen = 16;

		// create a 64-character formatted output line:
		sprintf(szBuf, "%c %08lX :                             "
			"                      "
			"   ", direction, pTmp - pAddress);
		lOutLen2 = lOutLen;

		for (lIndex = 1 + lIndent, lIndex2 = 53 - 15 + lIndent, lRelPos = 0;
			lOutLen2;
			lOutLen2--, lIndex += 2, lIndex2++
			)
		{
			ucTmp = *pTmp++;

			sprintf(szBuf + lIndex, "%02X ", (unsigned short)ucTmp);
			if (!isprint(ucTmp))  ucTmp = '.'; // nonprintable char
			szBuf[lIndex2] = ucTmp;

			if (!(++lRelPos & 7))     // extra blank after 4 bytes
			{
				lIndex++; szBuf[lIndex + 2] = ' ';
			}
		}

		if (!(lRelPos & 7)) lIndex--;

		szBuf[lIndex] = ' ';
		szBuf[lIndex + 1] = ' ';

		//file = fopen(dumpFileName, "a");
	 //   fprintf(file, "%s\n", szBuf);
		//fclose(file);
		Logger(m_dumpFileName, szBuf);

		buf.pData += lOutLen;
		buf.lSize -= lOutLen;
	}

	//file = fopen(dumpFileName, "a");
 //   fprintf(file, "\n");
	//fclose(file);
	Log(m_dumpFileName, "");

	//mutex.MutexOff();
}

int CJniorConnection::Send(char* bytes, int size)
{
	char* sendBytes = new char[size + 5];
	int m_pos = 0;

	sendBytes[m_pos++] = 0x01;
	setshort(sendBytes, m_pos, size);

	short crc = 0xffff;
	crc = crc16_update(0, bytes, size);
	setshort(sendBytes, m_pos, crc);

	memcpy((char*)&sendBytes[m_pos], bytes, size);

	// initialize a socket set.
	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(m_sckt, &fds);

	timeval timeout;
	timeout.tv_sec = 0;
	timeout.tv_usec = SEND_RECV_TIMEOUT;

	int isWritable = select(0, NULL, &fds, NULL, &timeout);
	if (isWritable != 1) printf("write select: %d\r\n", isWritable);

	int bytesSent = 0;

	if (isWritable == SOCKET_ERROR) {
		//return Error(WSAGetLastError());

		// try to reconnect
		//Reconnect();
	}

	// data is ready to be written to the socket
	else if (isWritable > 0) {
		bytesSent = send(m_sckt, sendBytes, size + 5, 0);

		if (bytesSent > 0) {
			//char timeStr [9];
			//_strtime( timeStr );
			//printf("%s  sent %d bytes\r\n", timeStr, bytesSent);

#if DEBUG
			if (m_dumpFileName != NULL) {
				hexdump(sendBytes, 0, size + 5, '<');
			}
#endif

			//if (this->m_communicationListeners.size() > 0 && bytesSent > 0) {
			//	COMMUNICATIONS_CALLBACK_ARGS* args = new COMMUNICATIONS_CALLBACK_ARGS;
			//	args->handle = this->m_handle;
			//	args->count = bytesSent;
			//	args->direction = 1;
			//	args->bytes = new char[bytesSent];
			//	memcpy(args->bytes, sendBytes, bytesSent);

			//	CALLBACK_WRAPPER* wrapper = new CALLBACK_WRAPPER;
			//	wrapper->listeners.assign(this->m_communicationListeners.begin(), this->m_communicationListeners.end());
			//	wrapper->args = args;

			//	////while (dwWrapperThreadId != -1);

			//	HANDLE hThread = CreateThread(
			//				NULL,										// default security attributes 
			//				0,											// use default stack size  
			//				(LPTHREAD_START_ROUTINE) CallbackWrapper,	// thread function 
			//				wrapper,									// argument to thread function 
			//				0,											// use default creation flags 
			//				&m_dwCommunicationsWrapperThreadId);								// returns the thread identifier 
			//}

			//m_lastSentTime = time(0);
		}

		// send failed
		else {
			//Error(WSAGetLastError());

			//// try to reconnect
			//Reconnect();

			// continute reading
			return STATUS_FAILED;
		}

		//m_lastSentTime = time(0);
	}

	delete sendBytes;

	return (bytesSent > 0) ? STATUS_OK : STATUS_FAILED;
}

int CJniorConnection::ProcessUsageMeterPacket() {
	char* bytes = new char[0xff];
	int m_pos = 0;

	bytes[m_pos++] = USAGE_METER_PACKET;

	for (int i = 0; i < 8; i++) {
		long long usage = m_jnior->GetInputUsageMeter(i);
		setlong(bytes, m_pos, usage);
	}

	for (int i = 0; i < 8; i++) {
		long long usage = m_jnior->GetOutputUsageMeter(i);
		setlong(bytes, m_pos, usage);
	}

	DWORD64 millis = GetMilliTime();
	setlong(bytes, m_pos, millis);

	int result = Send(bytes, m_pos);

	delete bytes;

	return result;
}

void CJniorConnection::PacketReceived(int messageType, HEADER recvHeader) {
	int length = (recvHeader.LENGTH_HI << 8) + recvHeader.LENGTH_LO;

	char log[128];
	sprintf(log, "Message Type=%d Length=%d", messageType, length);
	Log(m_dumpFileName, log);

	switch ((int)messageType) {
	case 126:		// login packet
	{
		int usernameLen = m_recvbuf[m_pos++];
		char* username = new char[usernameLen + 1];
		strncpy(username, (const char*)&m_recvbuf[m_pos], usernameLen);
		username[usernameLen] = '\0';
		m_pos += usernameLen;

		int passwordLen = m_recvbuf[m_pos++];
		char* password = new char[passwordLen + 1];
		strncpy(password, (const char*)&m_recvbuf[m_pos], passwordLen);
		password[passwordLen] = '\0';
		m_pos += passwordLen;

		sprintf(log, "Login %s:%s", username, password);
		Log(m_dumpFileName, log);

		if (strncmp("", username, usernameLen) == 0) {
			if (strncmp("am5pb3I6am5pb3I=", password, passwordLen) == 0) {
				BuildLoginPacket(128);
			}
			else if (strncmp("dXNlcjp1c2Vy", password, passwordLen) == 0) {
				BuildLoginPacket(64);
			}
			else if (strncmp("Z3Vlc3Q6Z3Vlc3Q=", password, passwordLen) == 0) {
				BuildLoginPacket(0);
			}
			else {
				BuildLoginPacket(-1);
			}
		}
		else if (strncmp("jnior", username, usernameLen) == 0 && strncmp("jnior", password, passwordLen) == 0) {
			BuildLoginPacket(128);
		}
		else if (strncmp("user", username, usernameLen) == 0 && strncmp("user", password, passwordLen) == 0) {
			BuildLoginPacket(64);
		}
		else if (strncmp("guest", username, usernameLen) == 0 && strncmp("guest", password, passwordLen) == 0) {
			BuildLoginPacket(0);
		}
		else {
			BuildLoginPacket(-1);
		}

		BuildMonitorPacket();
		BuildExtendedMonitorPacket();
	}
	break;

	case 10:		// command packet
	{
		int action = m_recvbuf[m_pos++];
		int channel;
		int channelMask;
		int statesMask;
		int duration;

		switch (action) {
		case 1:			// close output
			channel = getshort(m_recvbuf, m_pos) - 1;

			//#if DEBUG				
			sprintf(log, "Close output %d", channel);
			Log(m_dumpFileName, log);
			//#endif

			m_jnior->SetOutputs(1 << channel, 1 << channel);
			break;
		case 2:			// open output
			channel = getshort(m_recvbuf, m_pos) - 1;

			//#if DEBUG				
			sprintf(log, "Open output %d", channel);
			Log(m_dumpFileName, log);
			//#endif

			m_jnior->SetOutputs(1 << channel, 0);
			break;
		case 3:			// toggle output
			channel = getshort(m_recvbuf, m_pos) - 1;

			//#if DEBUG				
			sprintf(log, "Toggle output %d", channel);
			Log(m_dumpFileName, log);
			//#endif

			m_jnior->SetOutputs(1 << channel, m_jnior->GetOutputs() ^ (1 << channel));
			break;

		case 6:			// pulse output
			channel = getshort(m_recvbuf, m_pos) - 1;
			duration = getint(m_recvbuf, m_pos);

			//#if DEBUG				
			sprintf(log, "Pulse output %d for %d", channel, duration);
			Log(m_dumpFileName, log);
			//#endif

			m_jnior->PulseOutputs(1 << channel, m_jnior->GetOutputs() ^ (1 << channel), duration);
			break;

		case 7:			// block pulse outputs
			if (length == 8) {
				channelMask = m_recvbuf[m_pos++];
				statesMask = m_recvbuf[m_pos++];
				duration = getint(m_recvbuf, m_pos);

				//#if DEBUG				
				sprintf(log, "Pulse Outputs - Channels:%d States:%d for %d", channelMask & 0xff, statesMask & 0xff, duration);
				Log(m_dumpFileName, log);
				//#endif

				m_jnior->PulseOutputs(channelMask, statesMask, duration);
			}
			else if (length == 10) {
				channelMask = getshort(m_recvbuf, m_pos);
				statesMask = getshort(m_recvbuf, m_pos);
				duration = getint(m_recvbuf, m_pos);

				//#if DEBUG				
				sprintf(log, "Pulse Outputs - Channels:%d States:%d for %d", channelMask & 0xff, statesMask & 0xff, duration);
				Log(m_dumpFileName, log);
				//#endif

				m_jnior->PulseOutputs(channelMask, statesMask, duration);
			}
			break;

		case 10:		// block set outputs
			if (length == 4) {
				channelMask = m_recvbuf[m_pos++];
				statesMask = m_recvbuf[m_pos++];

				//#if DEBUG				
				sprintf(log, "Set Outputs - Channels:%d States:%d", channelMask & 0xff, statesMask & 0xff);
				Log(m_dumpFileName, log);
				//#endif

				m_jnior->SetOutputs(channelMask, statesMask);
			}
			else if (length == 6) {
				channelMask = getshort(m_recvbuf, m_pos);
				statesMask = getshort(m_recvbuf, m_pos);

				//#if DEBUG				
				sprintf(log, "Set Outputs - Channels:%d States:%d", channelMask & 0xff, statesMask & 0xff);
				Log(m_dumpFileName, log);
				//#endif

				m_jnior->SetOutputs(channelMask, statesMask);
			}
			break;
		}
	}
	break;

	case REQUEST_PACKET:
		switch (getshort(m_recvbuf, m_pos)) {
		case 2:
			ProcessUsageMeterPacket();
			break;
		}
		break;

		//	case READ_REG_KEYS_RESPONSE:
		//		ProcessReadRegistryKeysResponse();
		//		break;

		//	case WRITE_REG_KEYS_RESPONSE:
		//		{
		//			// get the number of keys returned
		//			int count = getshort(m_recvbuf, m_pos);

		//			m_writtenKeys -= count;

		//			printf("Sucessfully wrote %d key(s)\n", count);
		//		}

		//		break;

		//	case LIST_REG_KEYS_RESPONSE:
		//		{
		//			// get the number of keys returned
		//			int count = getshort(m_recvbuf, m_pos);

		//			// create a new array or strings to hold the listing
		//			char** keyNames = new char*[count];

		//			// get each key
		//			for (int i = 0; i < count; i++) {
		//				// get the string length
		//				int keyNameLength = m_recvbuf[m_pos++] & 0xff;
		//				// create a char array to hold the string
		//				keyNames[i] = new char[keyNameLength + 1];
		//				// copy the key over
		//				strncpy(keyNames[i], &m_recvbuf[m_pos], keyNameLength);
		//				keyNames[i][keyNameLength] = '\0';
		//				m_pos += keyNameLength;
		//			}

		//			if (this->m_registryListingListeners.size() > 0) {
		//				//m_RegistryCallback(regKeys, count);

		//				REGISTRY_LISTING_CALLBACK_ARGS* args = new REGISTRY_LISTING_CALLBACK_ARGS;
		//				args->handle = this->m_handle;
		//				args->count = count;
		//				args->path = this->m_currentListingRequest;
		//				args->keyNames = new char*[count];
		//				for (int i = 0; i < count; i++) {
		//					int keyNameLength = strlen(keyNames[i]);
		//					// create a char array to hold the string
		//					args->keyNames[i] = new char[keyNameLength + 1];
		//					// copy the key over
		//					strncpy(args->keyNames[i], keyNames[i], keyNameLength);
		//					args->keyNames[i][keyNameLength] = '\0';
		//				}

		//				CALLBACK_WRAPPER* wrapper = new CALLBACK_WRAPPER;
		//				wrapper->listeners.assign(this->m_registryListingListeners.begin(), this->m_registryListingListeners.end());
		//				wrapper->args = args;

		//				//while (dwWrapperThreadId != -1);

		//				HANDLE hThread = CreateThread(
		//							NULL,										// default security attributes 
		//							0,											// use default stack size  
		//							(LPTHREAD_START_ROUTINE) CallbackWrapper,	// thread function 
		//							wrapper,									// argument to thread function 
		//							0,											// use default creation flags 
		//							&m_dwRegistryWrapperThreadId);								// returns the thread identifier 
		//			}

		//			break;
		//		}

		//	case READ_DEVICE_RESPONSE:
		//		{
		//			int count = getshort(m_recvbuf, m_pos);

		//			for (int i = 0; i < count; i++) {
		//				long long deviceId = getlong(m_recvbuf, m_pos);
		//				int length = getshort(m_recvbuf, m_pos);


		//				CDevice* device = m_devices[deviceId];
		//				if (device == NULL) {
		//					if ((deviceId & 0xff) == DEVICE_TYPE_ENUM::FOUR_RELAY_OUT) {
		//						device = new CTypeFB();
		//					}
		//					if (device != NULL) {
		//						m_devices[deviceId] = device;
		//					}
		//				}

		//				device->Parse(m_recvbuf, m_pos);
		//			}

		//			break;
		//		}

		//	case GET_EXTERNAL_VALUE_RESPONSE:
		//		{
		//			char deviceType = m_recvbuf[m_pos++];
		//			char ioSelection = m_recvbuf[m_pos++];
		//			char channelNumber = m_recvbuf[m_pos++];
		//			double value = getdouble(m_recvbuf, m_pos);

		//			short externalModuleValueKey = (deviceType << 8) + (ioSelection << 4) + channelNumber;
		//			m_externalModuleValues[externalModuleValueKey] = value;

		//			break;
		//		}

		//	case LOGIN_ACK:
		//		ProcessLoginResponse();

		//		break;

		//	case CUSTOM_COMMAND_RESPONSE:
		//		{
		//			char returnStatus = m_recvbuf[m_pos++];
		//			short payloadSize = getshort(m_recvbuf, m_pos);
		//			if (payloadSize > 0) {
		//				char* payload = new char[payloadSize + 1];
		//				for (int i = 0; i < payloadSize; i++) {
		//					payload[i] = m_recvbuf[m_pos++];
		//				}

		//				if (this->m_customCommandListeners.size() > 0) {
		//					CUSTOM_COMMAND_CALLBACK_ARGS* args = new CUSTOM_COMMAND_CALLBACK_ARGS;
		//					args->handle = this->m_handle;
		//					args->returnStatus = returnStatus;
		//					args->size = payloadSize;
		//					args->payload = payload;

		//					CALLBACK_WRAPPER* wrapper = new CALLBACK_WRAPPER;
		//					wrapper->listeners.assign(this->m_customCommandListeners.begin(), this->m_customCommandListeners.end());
		//					wrapper->args = args;

		//					//while (dwWrapperThreadId != -1);

		//					HANDLE hThread = CreateThread(
		//								NULL,										// default security attributes 
		//								0,											// use default stack size  
		//								(LPTHREAD_START_ROUTINE) CallbackWrapper,	// thread function 
		//								wrapper,									// argument to thread function 
		//								0,											// use default creation flags 
		//								&m_dwRegistryWrapperThreadId);								// returns the thread identifier 
		//				}
		//				//memcpy(payload, (char*)m_recvbuf[m_pos], payloadSize);
		//			}

		//			break;
		//		}

	default:
		m_pos += length - 1;

		break;
	}
}


void CJniorConnection::DataReady() {
	char log[128];

	//char txt[100];
	//sprintf(txt, "PREREAD : nextReadPos=%d, m_pos=%d", m_nextReadPos, m_pos);
	//Log(txt);

	int bytesRecv = recv(m_sckt, &m_recvbuf[m_nextReadPos], 0xffff - m_nextReadPos, 0);
	// nothing was read
	if (bytesRecv == SOCKET_ERROR) {
		int error = WSAGetLastError();
		return;
	}

	//// call the communications callback wrapper incase someone is listening
	//if (this->m_communicationListeners.size() > 0 && bytesRecv > 0) {
	//			COMMUNICATIONS_CALLBACK_ARGS* args = new COMMUNICATIONS_CALLBACK_ARGS;
	//			args->handle = this->m_handle;
	//			args->bytes = new char[bytesRecv];
	//			memcpy(args->bytes, &m_recvbuf[m_nextReadPos], bytesRecv);
	//			args->count = bytesRecv;
	//			args->direction = -1;

	//			CALLBACK_WRAPPER* wrapper = new CALLBACK_WRAPPER;
	//			wrapper->listeners.assign(this->m_communicationListeners.begin(), this->m_communicationListeners.end());
	//			wrapper->args = args;

	//			//while (dwWrapperThreadId != -1);

	//			//this->hexdump(&m_recvbuf[m_nextReadPos], bytesRecv, -1);

	//			HANDLE hThread = CreateThread(
	//						NULL,										// default security attributes 
	//						0,											// use default stack size  
	//						(LPTHREAD_START_ROUTINE) CallbackWrapper,	// thread function 
	//						wrapper,									// argument to thread function 
	//						0,											// use default creation flags 
	//						&m_dwCommunicationsWrapperThreadId);								// returns the thread identifier 
	//}


	if (bytesRecv > 0) {
		m_nextReadPos += bytesRecv;
	}

#if DEBUG
	char s[100];
	sprintf(s, "read %d bytes\r\n", bytesRecv);
	Log(m_dumpFileName, s);
	if (bytesRecv > 0) {
		printf("%s", s);
		if (m_dumpFileName != NULL) {
			hexdump(&m_recvbuf[0], 0, m_nextReadPos, '>');
		}
	}
#endif

	// hmm if the WSAWaitForMultipleEvents passed through then 
	// there is either a read or close event.  If there is no data 
	// read then it must have been a close event?
	if (bytesRecv <= 0) {
		//Error(WSAGetLastError());

		// try to reconnect
		closesocket(this->m_sckt);

		// continute reading
		return;
	} // bytesrecv = -1

	else if (m_nextReadPos > m_pos) {
		int available;
		while (m_nextReadPos > m_pos) {

			Sleep(10);

			if (this->m_quit) {
				m_nextReadPos = m_pos = 0;
				m_recvState = WAIT_SYNC_BYTE;
				break;
			}

#if DEBUG
			char txt[100];
			sprintf(txt, "recvState=%d, nextReadPos=%d, m_pos=%d", m_recvState, m_nextReadPos, m_pos);
			Log(m_dumpFileName, txt);
			//if (m_dumpFileName != NULL) {
			//	hexdump(&m_recvbuf[0], m_nextReadPos, '>');
			//}
#endif

			switch (m_recvState) {
			case WAIT_SYNC_BYTE:
				m_recvHeader.OFFSET = m_pos;
				if ((m_recvHeader.SYNC_BYTE = m_recvbuf[m_pos++]) == 0x01) {
					m_recvState++;
				}
				m_recvHeader.PAYLOAD_LENGTH = 0;

#if DEBUG
				sprintf(log, "SYNC BYTE? (%d)", m_recvHeader.SYNC_BYTE);
				Log(m_dumpFileName, log);
#endif

				// if we never find the sync byte but weve read all of the 
				// bytes then we should reset the buffer array
				if (m_pos == m_nextReadPos) {
					m_nextReadPos = 0;
					m_pos = 0;
				}

				//printf("SYNC BYTE=%d\n", m_recvHeader.SYNC_BYTE);

				break;

			case WAIT_LENGTH_HI:
				m_recvHeader.LENGTH_HI = m_recvbuf[m_pos++] & 0xff;
				m_recvState++;

				//printf("LENGTH_HI=%d\n", m_recvHeader.LENGTH_HI);

				break;

			case WAIT_LENGTH_LO:
				m_recvHeader.LENGTH_LO = m_recvbuf[m_pos++] & 0xff;
				m_recvState++;

				//printf("LENGTH_LO=%d\n", m_recvHeader.LENGTH_LO);

				break;

			case WAIT_CRC_HI:
				m_recvHeader.CRC_HI = m_recvbuf[m_pos++] & 0xff;
				m_recvState++;

				//printf("CRC_HI=%d\n", m_recvHeader.CRC_HI);

				break;

			case WAIT_CRC_LO:
				m_recvHeader.CRC_LO = m_recvbuf[m_pos++] & 0xff;
				m_recvState++;

				//printf("CRC_LO=%d\n", m_recvHeader.CRC_LO);

				break;

			case WAIT_PAYLOAD:
				available = m_nextReadPos - m_pos;

				//char str[100];
				//sprintf(str, "offset=%d, payload length=%d, avail=%d, bytes recv=%d, m_pos=%d\n", 
				//	m_recvHeader.OFFSET, m_recvHeader.LENGTH_HI << 8 + m_recvHeader.LENGTH_LO, available, bytesRecv, m_pos);

				//Log("Wait Payload");
				//Log(str);
				//printf(str);

				//hexdump(&m_recvbuf[0], m_pos + m_recvHeader.LENGTH_HI << 8 + m_recvHeader.LENGTH_LO, '>');

				if (available >= m_recvHeader.LENGTH_HI << 8 + m_recvHeader.LENGTH_LO) {
					m_recvHeader.PAYLOAD_LENGTH = m_recvHeader.LENGTH_HI << 8 + m_recvHeader.LENGTH_LO;
					memcpy(&(m_recvHeader.PAYLOAD)[m_recvHeader.PAYLOAD_LENGTH], &m_recvbuf[m_pos], available);
					m_recvState++;
					break;
				}

				else {
					m_recvHeader.PAYLOAD_LENGTH += available;
				}

				break;

			case PAYLOAD_READY:
			{
				// here we would check the CRC and back the offset up to try again.
				int length = (m_recvHeader.LENGTH_HI << 8) + m_recvHeader.LENGTH_LO;
				unsigned short crc = crc16_update(0, &m_recvbuf[m_pos], length);
				unsigned short jniorCrc = (m_recvHeader.CRC_HI << 8) + m_recvHeader.CRC_LO;

				if (jniorCrc != 0xffff && crc != jniorCrc) {
					// INVALID CRC
					m_pos = m_recvHeader.OFFSET + 1;
					m_recvState = WAIT_SYNC_BYTE;

					sprintf(log, "Invalid CRC %d, should be %d", jniorCrc, crc);
					Log(m_dumpFileName, log);

					continue;
				}

				//char str[100];
				//sprintf(str, "offset=%d, payload length=%d, avail=%d, bytes recv=%d, m_pos=%d\n", 
				//	m_recvHeader.OFFSET, m_recvHeader.LENGTH_HI << 8 + m_recvHeader.LENGTH_LO, available, bytesRecv, m_pos);

				//Log("Payload Ready");
				//Log(str);
				//printf(str);

				/*if (dumpFileName != NULL) {
					hexdump(&m_recvbuf[0], nextReadPos, '>');
				}*/

				int messageType = (int)(m_recvbuf[m_pos++] & 0xff);
				PacketReceived(messageType, m_recvHeader);
				//Status(PACKET_RECEIVED_START + messageType);

				m_recvState = WAIT_SYNC_BYTE;

				if (m_pos == m_nextReadPos) {
					m_nextReadPos = 0;
					m_pos = 0;
				}
				else {
					cout << "waiting for more data" << endl;
				}

				break;
			}

			} // switch

		} // while m_pos < bytesRecv
	}

}

void CJniorConnection::Log(char* text) {
	Logger(this->m_dumpFileName, text);
}

void CJniorConnection::Log(char* filename, char* text) {
	if (filename != NULL) {
		Logger(filename, text);
	}
}

//void CJniorConnection::Log(CJniorConnection* jnior, char* filename, char* text) {
//	jnior->m_mutex.MutexOn();
//	
//	if (filename != NULL) {
//		FILE* file = NULL;
//		int tries = 100;
//		while (file == NULL) {
//			file = fopen(filename, "a");
//			if (--tries == 0) {
//				return;
//			}
//		}
//
//		// create the timestamp
//		char timestamp[32];
//		SYSTEMTIME lt;
//		GetLocalTime(&lt);
//		sprintf(timestamp, "%02d:%02d:%02d.%03d", lt.wHour, lt.wMinute, lt.wSecond, lt.wMilliseconds);
//		fprintf(file, "%s   %s\r\n", timestamp, text);
//		fclose(file);
//	}
//	  cout << text << endl;
//
//	jnior->m_mutex.MutexOff();
//}

int CJniorConnection::SetDumpFile(char* filename) {
	m_dumpFileName = new char[strlen(filename) + 1];
	strcpy(m_dumpFileName, filename);

	Logger(filename, "**********************************************************************");

	return 0;
}


#include "SYS/TIMEB.h" 

DWORD64 GetMilliTime(void)
{
	DWORD64 dMilliseconds;
	int iMilliSeconds;
	_timeb tSysTime;

	_ftime(&tSysTime);
	dMilliseconds = tSysTime.time;
	iMilliSeconds = tSysTime.millitm;
	dMilliseconds *= 1000;
	dMilliseconds += iMilliSeconds;
	return dMilliseconds;
}