#include "mungal4.h"
#include <windows.h>
#include "L4SERIAL.h"
#include "L4PCSPAK.h"
#include <assert.h>

//Win32 Serial support: ADB 02/10/07
//------------------------------------------------
// COM_INIT macro
// Initializes COMBUF structure
//------------------------------------------------
void PCSerialPacket::COM_INIT(PCSerPackBuf* buffer)
{
   WaitForSingleObject(hMutex, INFINITE);
	buffer->head = 0;
	buffer->tempHead = 0;
	buffer->tempTail = 0;
	buffer->tail = 0;

	buffer->count = 0;
	buffer->tempIn = 0;
	buffer->tempOut = 0;
   ReleaseMutex(hMutex);
}

//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_PUT_AL macro
// 'Temporarily' puts character in AL into COMBUF
//------------------------------------------------
void PCSerialPacket::COM_PUT_AL(PCSerPackBuf* buffer, BYTE al)
{
   WaitForSingleObject(hMutex, INFINITE);
	if(buffer->tempIn > COMBUFSIZE)
	{
#if DIE_ON_OVERFLOW
		DISABLE_AND_DIE("Serial Packet Buffer Overflow!");
#else
      ReleaseMutex(hMutex);
		return;
#endif
	}

	buffer->tempIn++;						//increment 'tempIn' count

	buffer->data[buffer->tempHead] = al;	//write the data

	buffer->tempHead++;						//bump 'tempHead'
	buffer->tempHead &= (COMBUFSIZE-1);		//limit it
   ReleaseMutex(hMutex);
}

//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_RESTORE_IN macro
// Discards characters 'temporarily' placed in COMBUF
//------------------------------------------------
void PCSerialPacket::COM_RESTORE_IN(PCSerPackBuf* buffer)
{
   WaitForSingleObject(hMutex, INFINITE);
	buffer->tempIn = 0;					//clear the 'tempIn' count
	buffer->tempHead = buffer->head;	//restore 'tempHead' index
   ReleaseMutex(hMutex);
}

//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_ACCEPT_IN macro
// Accepts characters 'temporarily' placed in COMBUF
//------------------------------------------------
void PCSerialPacket::COM_ACCEPT_IN(PCSerPackBuf* buffer)
{
   WaitForSingleObject(hMutex, INFINITE);
	buffer->count += buffer->tempIn;				//add to current count
	buffer->tempIn = 0;

	buffer->head = buffer->tempHead;				//set the new head offset
   ReleaseMutex(hMutex);
}

//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_GET_AL macro
// 'Temporarily' gets character into AL from COMBUF
//------------------------------------------------
int PCSerialPacket::COM_GET_AL(PCSerPackBuf* buffer)
{
   WaitForSingleObject(hMutex, INFINITE);
	buffer->tempOut++;							//inc 'tempOut' count

	int al = buffer->data[buffer->tempTail];	//read the data

	buffer->tempTail ++;						//bump 'tempTail'
	buffer->tempTail &= (COMBUFSIZE-1);			//limit it

   ReleaseMutex(hMutex);
	return al;
}

//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_RESTORE_OUT macro
// Restores characters 'temporarily' removed from COMBUF
//------------------------------------------------
void PCSerialPacket::COM_RESTORE_OUT(PCSerPackBuf* buffer)
{
   WaitForSingleObject(hMutex, INFINITE);
	buffer->tempOut = 0;				//clear the 'tempOut' count

	buffer->tempTail = buffer->tail;	//restore 'tempTail' index
   ReleaseMutex(hMutex);
}

//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_ACCEPT_OUT macro
// Discards characters 'temporarily' removed from COMBUF
//------------------------------------------------
void PCSerialPacket::COM_ACCEPT_OUT(PCSerPackBuf* buffer)
{
   WaitForSingleObject(hMutex, INFINITE);
	buffer->count -= buffer->tempOut;	//subtract from current count
	buffer->tempOut = 0;				//clear 'tempOut' count

	buffer->tail = buffer->tempTail;	//set the new tail offset
   ReleaseMutex(hMutex);
}

//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_EAT_PACKET macro
// Discards entire buffer item from COMBUF
//------------------------------------------------
void PCSerialPacket::COM_EAT_PACKET(PCSerPackBuf* buffer)
{
   WaitForSingleObject(hMutex, INFINITE);
	BYTE command = buffer->data[buffer->tail];	//read the packet command byte

	command &= 0x07;							//remove the MSB
	int length = this->rxLengthPtr[command];	//convert packet command to length
	length++;									//include the command byte
	buffer->count -= length;					//subtract from count

	buffer->tail += length;						//move the tail
	buffer->tail &= (COMBUFSIZE-1);				//limit it
	buffer->tempTail = buffer->tail;			//set the new tempTail index

	buffer->tempOut = 0;
   ReleaseMutex(hMutex);
}

//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_SPACE_IN macro
// Returns input bytes available in COMBUF
//------------------------------------------------
int PCSerialPacket::COM_SPACE_IN(PCSerPackBuf* buffer)
{
   WaitForSingleObject(hMutex, INFINITE);
	int available = COMBUFSIZE;
	available -= buffer->count;
   available -= buffer->tempIn;
   ReleaseMutex(hMutex);
	return available;
}

//Win32 Serial support: ADB 02/13/07
//------------------------------------------------
// COM_SPACE_OUT macro
// Returns output bytes available in COMBUF
//------------------------------------------------
int PCSerialPacket::COM_SPACE_OUT(PCSerPackBuf* buffer)
{
   WaitForSingleObject(hMutex, INFINITE);
	int output = buffer->count;
   output -= buffer->tempOut;
   ReleaseMutex(hMutex);
	return output;
}

//Win32 Serial support: ADB 06/23/07
PCSerialPacket::PCSerialPacket()
{
}

void PCSerialPacket::ShutdownRxThread()
{
   //Win32 Serial support: ADB 06/23/07
   enabled = false;
	bThreadAbort = TRUE;
   if (overlapSend.hEvent != INVALID_HANDLE_VALUE)
   {
      CloseHandle(overlapSend.hEvent);
      overlapSend.hEvent = INVALID_HANDLE_VALUE;
   }
	if(hRxAbortEvent != INVALID_HANDLE_VALUE)
	{
		SetEvent(hRxAbortEvent);
		CloseHandle(hRxAbortEvent);
		hRxAbortEvent = INVALID_HANDLE_VALUE;
	}
	if(hRxThread != INVALID_HANDLE_VALUE)
	{
      WaitForSingleObject(hRxThread, INFINITE);
		CloseHandle(hRxThread);
		hRxThread = INVALID_HANDLE_VALUE;
	}
   if(hMutex != INVALID_HANDLE_VALUE)
   {
      CloseHandle(hMutex);
      hMutex = INVALID_HANDLE_VALUE;
   }
	if(hComm != INVALID_HANDLE_VALUE)
	{
		CloseHandle(hComm);
		hComm = INVALID_HANDLE_VALUE;
	}
}

PCSerialPacket::~PCSerialPacket()
{
	if (this == NULL)
	{
		std::cout << "~PCSerialPacket() has been passed a NULL pointer\n" << std::flush;
	}
	else
	{
		ShutdownRxThread();
	}
}

//Win32 Serial support: ADB 01/15/07
// Only call Initialize(...) ONCE during the lifetime of the object!
int PCSerialPacket::Initialize(WORD rate, WORD format, const char* port, BYTE* rxLengths, BYTE rxCount, BYTE* txLengths, BYTE txCount)
{
	//;-------------------------------------------------------------------------
	//; PCSPAKInit()
	//; Constructor for serial packet driver interface
	//;-------------------------------------------------------------------------
	//; Enter:
	//;		init_this		= pointer to object
	//;		init_port		= address of serial port
	//;		init_irq_num	= interrupt number
	//;		init_rx_len 	= pointer to (byte) receive length table
	//;		init_tx_len 	= pointer to (byte) transmit length table
	//;-------------------------------------------------------------------------
	//; Returns:
	//;		 1 if ok,
	//;		-1 if IRQ's all used,
	//;		-2 if DPMI error,
	//;		-3 if really weird bogus error,
	//;		else IIR value
	//;-------------------------------------------------------------------------

	//DWORD return_status = 0;

	//;--------------------------------------------------------------
	//; Clear the data buffers
	//;--------------------------------------------------------------
	COM_INIT(&rxBuf);	//assumes esi points to object
	COM_INIT(&txBuf);	//assumes esi points to object
	//;--------------------------------------------------------------
	//; Initialize values
	//;--------------------------------------------------------------
	enabled = 0;
	txHoldOff = 0;
	txReply = 0;
	rxFlags = 0;
	Error = 0;
	intDataPtr = 0;

	historyIndex = 0;

	txState = TX_IDLE_STATE;	//initialize to idle state
	rxState = RX_IDLE_STATE;	//initialize to idle state


	txLengthPtr = txLengths;	//set dialect transmit table pointer
	txMaximum = txCount;		//set maximum dialect transmit index

	rxLengthPtr = rxLengths;	//set dialect receive table pointer
	rxMaximum = rxCount;		//set maximum dialect receive index

   hComm = INVALID_HANDLE_VALUE;
	hRxAbortEvent = INVALID_HANDLE_VALUE;
	hRxThread = INVALID_HANDLE_VALUE;
   hMutex = INVALID_HANDLE_VALUE;
	bThreadAbort = FALSE;
   overlapSend.hEvent = INVALID_HANDLE_VALUE;

	hComm = InitSerialPort(rate, format, port, true);
	if(hComm == INVALID_HANDLE_VALUE)
	{
		enabled = 0;
		DWORD err = GetLastError();
		DEBUG_STREAM << "ERROR: InitSerialPort() failed [GetLastError() = " << GetLastError() << "]!\n" << std::flush;
		return PCSPAKWin32Err;
	}
	enabled = 1;

	//Setup multi-threading for rx thread
   hMutex = CreateMutex(NULL, false, NULL);
   if(hMutex == NULL)
   {
      DEBUG_STREAM << "ERROR: CreateMutex() failed [GetLastError() = " << GetLastError() << "]!\n" << std::flush;
      enabled = 0;
      return PCSPAKWin32Err;
   }

	if(!SetCommMask(hComm, EV_RXCHAR))
	{
		DEBUG_STREAM << "ERROR: SetCommMask() failed [GetLastError() = " << GetLastError() << "]!\n" << std::flush;
		enabled = 0;
		return PCSPAKWin32Err;
	}
	hRxAbortEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

	hRxThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(&RxThreadProc), this, NULL, NULL);
	if(hRxThread == NULL)
	{
		DEBUG_STREAM << "ERROR: CreateThread() failed [GetLastError() = " << GetLastError() << "]!\n" << std::flush;
		enabled = 0;
		return PCSPAKWin32Err;
	}
	SetThreadPriority(hRxThread, THREAD_PRIORITY_TIME_CRITICAL);

   overlapSend.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
   overlapSend.Internal = 0;
   overlapSend.InternalHigh = 0;
   overlapSend.Offset = 0;
   overlapSend.OffsetHigh = 0;

	return PCSPAKInitOk;
 }

//Win32 Serial support: ADB 01/15/07
Logical PCSerialPacket::TestInstance()
{
	//return PCSPAKTestInstance(this);
	return 1;
}

//Win32 Serial support: ADB 02/10/07
//-------------------------------------------------------------------------
// PCSPAKReceive()
// Returns packet if available
//-------------------------------------------------------------------------
// Enter:
//		BYTE *destPtr	= pointer to 128-byte destination buffer
//-------------------------------------------------------------------------
// Returns:
//		  int packetSize (or zero if no packet available)
//-------------------------------------------------------------------------
int PCSerialPacket::ReceivePacket(BYTE *destPtr)			// returns len (zero if none)
{
	if(!enabled)
		return 0;

	//--------------------------------------------------------------
	// Check character count: non-zero indicates entire packet
	//--------------------------------------------------------------
	if(rxBuf.count == 0)
		return 0;

	//--------------------------------------------------------------
	// Get buffer length
	//--------------------------------------------------------------
	BYTE c = COM_GET_AL(&rxBuf);			//get character into al
	//--------------------------------------------------------------
	// Verify first char is a valid command character
	//--------------------------------------------------------------
	if((c & 0x80) == 0)
	{
		DISABLE_AND_DIE("Character was not a command character!");
		return 0;
	}

   BYTE *overflow = destPtr + COMBUFSIZE - 1;

	*destPtr = c;							//save it
	destPtr++;
	BYTE length = rxLengthPtr[c & 0x7F];	//translate into length
	int count = length;						//save in edx
	count++;								//return at least one character
   assert(length < 256);
	//--------------------------------------------------------------
	// Transfer data from buffer
	//--------------------------------------------------------------
	do
	{
		c = COM_GET_AL(&rxBuf);				//get character into al
		*destPtr = c;						//save it
		destPtr++;
      assert(destPtr < overflow);
	}while(--length > 0);

	COM_ACCEPT_OUT(&rxBuf);					//accept all 'taken' characters

	return count;
}

//Win32 Serial Support
//-------------------------------------------------------------------------
// PCSPAKSend()
// Transmits packet
//-------------------------------------------------------------------------
// Enter:
//		BYTE *sp_src = pointer to (up to) 128-byte source buffer
//-------------------------------------------------------------------------
// Returns:
//		void
//-------------------------------------------------------------------------
void PCSerialPacket::SendPacket(BYTE *srcPtr)				//sends packet
{
	//--------------------------------------------------------------
	// Wait for sufficient room
	//--------------------------------------------------------------
	if(!enabled)							//if disabled, don't even try
		return;

	BYTE cmd = *srcPtr;						//get the command byte
	cmd &= 0x7F;							//remove MSB
	int length = txLengthPtr[cmd];			//get the length in al
	length++;								//add one for command byte

	//This code has been replaced for your sanity:
	//while(COM_SPACE_IN(&txBuf) < length)	//enough room?
	//	DISABLE_AND_DIE("Not enough space in serial packet buffer");

	////--------------------------------------------------------------
	//// Copy data into tx buffer
	////--------------------------------------------------------------
	//do
	//{
	//	COM_PUT_AL(&txBuf, *srcPtr);		//place it in the buffer
	//	srcPtr++;							//increment the source pointer
	//}
	//while(--length > 0);

	//COM_ACCEPT_IN(&txBuf);					//accept all 'placed' characters
	//
	////call	StartSending				;attempt to start tx interrupt

	//Sane code:
	BYTE *buffer = new BYTE[length + 1];//add one for the checksum
	memcpy(&buffer[0], srcPtr, length);//copy packet
	buffer[length] = 0;//initialize checksum
	for(int b=0; b<length; b++)//for each byte
		buffer[length] += buffer[b];//add to checksum;
	buffer[length] &= 0x007F;//limit checksum;

	if(!WriteFile(hComm, &buffer[0], length + 1, NULL, &overlapSend))
   {
      DWORD errNum = GetLastError();
      if (errNum != ERROR_IO_PENDING)
      {
         DEBUG_STREAM << "Could not begin writing data to serial port (GetLastError() = " << GetLastError() << ")!\n" << std::flush;
         delete buffer;
         return;
      }
   }
	DWORD bytesWritten;
	GetOverlappedResult(hComm, &overlapSend, &bytesWritten, TRUE);
	if(bytesWritten != length + 1)
	{
		DEBUG_STREAM << "Not all data was written to serial port!\n" << std::flush;
	}
	delete buffer;
}

//Win32 Serial support: ADB 01/15/07
int PCSerialPacket::ReceiveQueueCount()
{
	//return PCSPAKReceiveCount(this);
	return rxBuf.count;
}

//Win32 Serial support: ADB 01/15/07
int PCSerialPacket::TransmitQueueCount()
{
	//return PCSPAKTransmitCount(this);
	return txBuf.count;
}

//Win32 Serial Support: ADB 02/25/2007
void PCSerialPacket::SetDTR(Logical on)
{
	EscapeCommFunction(hComm, on ? SETDTR : CLRDTR);
}

//Win32 Serial support: ADB 01/15/2007
//-------------------------------------------------------------------------
// PCSPAKActive()
// Returns transmitter 'active' state (=0 when all transmission is done).
//-------------------------------------------------------------------------
// This function is now deprecated.
int PCSerialPacket::IsActive()
{
	//return PCSPAKActive(this);
	return enabled;
}

//Win32 Serial support: ADB 01/15/07
Word PCSerialPacket::Errors()// returns error value, clears it
{
	//return PCSPAKErrors(this);
	return Error;
}

//Win32 Serial support: ADB 06/25/07
//The folowing code has been ommited for your sanity
//-------------------------------------------------------------------------
// SendChar - send character through UART
// ...called from the interrupt routine and PCSPAK::sendCommand.
//-------------------------------------------------------------------------
// Enter:
//		  ESI = pointer to PCSPAK structure
//		  DX  = UART base address
//-------------------------------------------------------------------------
// Returns:
//		  (void)
//-------------------------------------------------------------------------
// Side effects:
//		  registers EAX, EBX, ECX modified
//-------------------------------------------------------------------------
//	void SendChar()
//	{
//		//--------------------------------------------------------------
//		// Send reply character if one is waiting
//		//--------------------------------------------------------------
//		if(txReply == 0)//"reply" character waiting?
//			goto noReply;//no, continue
//
//		TransmitCommChar(hComm, txReply);//yes, send the reply character
//		txReply = 0;//get and clear the "reply" character
//		goto sendDone;//done for now.
//noReply:
//		//--------------------------------------------------------------
//		// Enter the appropriate state handler
//		//--------------------------------------------------------------
//		if(txState == TX_IDLE_STATE)//idle state?
//			goto txIdleState;//yes, go to it
//
//		if(txState == TX_BODY_STATE)//"body" state?
//			goto txBodyState;//yes, go to it
//
//		if(txState == TX_WAIT_STATE)//"wait" state?
//			goto txWaitState;//yes, go to it
//
//		if(txState == TX_RESTART_STATE)//restart state?
//			goto txRestartState;//yes, go to it
//
//		DISABLE_AND_DIE("Unknown Send State!");
//		goto txWaitState;//must be "wait" state
//		//--------------------------------------------------------------
//		// Transmitter idle state
//		//--------------------------------------------------------------
//txIdleState:
//		if(txHoldOff != 0)//If transmitter 'held off', do nothing
//			goto txIsIdle;
//
//		if(txBuf.count != 0)//input buffer available?
//			goto txIdleAvail;//yes, send something
//txIsIdle:
//		//nothing to send, just return
//		goto sendDone;
//
//txIdleAvail:
//		//---------------------------
//		// Buffer ready, start
//		//---------------------------
//		txResetCount = TXMAXRESET;//set the 'reset' counter
//		txErrorCount = TXMAXERROR;//set the 'error' counter
//
//		//...deliberately fall through into 'txRestartState'
//
//		//--------------------------------------------------------------
//		// Transmitter restart state
//		//--------------------------------------------------------------
//txRestartState:
//		BYTE al = COM_GET_AL(&txBuf);//assumes esi points to object
//
//		if(al <= 0x007F)//is it a command?
//			goto txRestartBad;//no, discard it
//
//		BYTE ah = al & 0x007F;//remove MSB
//		if(ah < txMaximum)//legitimate command?
//			goto txRestartOk;//yes
//
//txRestartBad:
//		DISABLE_AND_DIE("Unknown Command In Send Queue!");
//		COM_ACCEPT_OUT(&txBuf);//eat the character
//		txState = TX_IDLE_STATE;//move to the "idle" state
//		goto sendDone;
//
//txRestartOk:
//		TransmitCommChar(hComm, al);//transmit the command character
//		txChecksum = al;//begin a new checksum count
//
//		//look up length
//		//include the command byte
//		txCount = txLengthPtr[ah] + 1;//save the length
//
//		txState = TX_BODY_STATE;//move to the "body" state
//		goto sendDone;
//
//		//--------------------------------------------------------------
//		// Transmitter "body" state
//		// Send message body, then checksum (unless interrupted by ACK/NAK)
//		//--------------------------------------------------------------
//txBodyState:
//		//---------------------------
//		// Interrupted by ACK/NAK?
//		//---------------------------
//		if(rxFlags == 0)//flag set?
//			goto txBodySend;//no, send normal body
//
//		COM_RESTORE_OUT(&txBuf);//interrupted, reset 'out' pointer
//
//		BYTE flags = rxFlags;//get flags
//		rxFlags = 0;//clear them
//
//		if((flags & RX_ACK_BIT) != 0)//was it an ACK?
//			goto txBodyNak;//no
//		Error |= TX_EARLY_ERR;//yes, set error flag
//txBodyNak:
//		//---------------------------
//		// Restart or die
//		//---------------------------
//		txResetCount--;//check the restart count
//		if(txResetCount <= 0)
//			goto txBodyGiveUp;//if too many errors, give up
//		//---------------------------
//		// Attempt restart
//		//---------------------------
//		TransmitCommChar(hComm, (BYTE)RESTART_CHAR);//no, send restart character
//
//		txState = TX_RESTART_STATE;//move to the "restart" state
//		goto sendDone;
//		//---------------------------
//		// Too many errors, die!
//		//---------------------------
//txBodyGiveUp:
//		Error |= TX_ABANDON;//set error flag
//		COM_EAT_PACKET(&txBuf);//discard current packet
//		goto txIdleState;//start another packet
//		//---------------------------
//		// Send body or checksum
//		//---------------------------
//txBodySend:
//		txCount--;//is buffer done?
//		if(txCount <= 0)
//			goto txBodyCheck;//yes, send checksum
//		//---------------------------
//		// Send a body character
//		//---------------------------
//		al = COM_GET_AL(&txBuf);//get a character from the buffer
//
//		if((al & 0x007F) == 0)//legitimate body char?
//			goto txBodyOk;//yes, continue
//		DISABLE_AND_DIE("Invalid Body Char In Send Queue!");
//		al &= 0x007F;//prevent bad data from becoming command
//txBodyOk:
//		TransmitCommChar(hComm, al);
//		txChecksum += al;//add to the checksum
//		goto sendDone;
//		//---------------------------
//		// End of body, send checksum
//		//---------------------------
//txBodyCheck:
//		al = txChecksum;//get the checksum
//		al &= 0x007F;//limit it
//		TransmitCommChar(hComm, al);//send it
//		//---------------------------
//		// prepare for "wait" state
//		//---------------------------
//		txIdleCount = TXMAXIDLE;//set the idle counter
//		txState = TX_WAIT_STATE;//move to the "wait" state
//		goto sendDone;
//
//		//--------------------------------------------------------------
//		// Transmitter "wait" state
//		// Waits for reply from other end
//		//--------------------------------------------------------------
//txWaitState:
//		flags = rxFlags;//get flags, also clear them
//		rxFlags = 0;
//		if(flags == 0)//flags clear?
//			goto txWaitMore;//yes, keep waiting
//
//		if((flags & RX_ACK_BIT) == 0)//ACK?
//			goto txWaitNak;//no, process NAK
//		//---------------------------
//		// ACK, buffer received OK
//		//---------------------------
//		COM_ACCEPT_OUT(&txBuf);//yes, good transmission. Discard packet.
//		txState = TX_IDLE_STATE;//move to the "idle" state
//		goto txIdleState;//start another buffer
//		//---------------------------
//		// NAK!
//		//---------------------------
//txWaitNak:
//		Error |= TX_NAK;//set error flag
//		COM_RESTORE_OUT(&txBuf);//restore output pointers
//
//		txErrorCount--;//check the error count
//		if(txErrorCount == 0)
//			goto txWaitGiveUp;//if too many errors, give up
//		goto txRestartState;//no, resend it
//		//---------------------------
//		// Keep waiting
//		//---------------------------
//txWaitMore:
//		txIdleCount--;//have we waited too long?
//		if(txIdleCount == 0)
//			goto txWaitDone;//yes, attempt to restart
//
//		TransmitCommChar(hComm, (BYTE)IDLE_CHAR);//no, send idle character
//		goto sendDone;
//		//---------------------------
//		// Attempt restart
//		//---------------------------
//txWaitDone:
//		Error |= TX_RESTART;//set error flag
//		COM_RESTORE_OUT(&txBuf);//restore output pointers
//
//		txResetCount--;//too many restarts?
//		if(txResetCount == 0)
//			goto txWaitGiveUp;//yes, abandon buffer
//
//		TransmitCommChar(hComm, (BYTE)RESTART_CHAR);//no, send restart character
//
//		txIdleCount = TXMAXIDLE;//reset the idle counter
//		txState = TX_RESTART_STATE;//move to the "idle" state
//		goto sendDone;
//		//---------------------------
//		// Give up
//		//---------------------------
//txWaitGiveUp:
//		Error |= TX_ABANDON;//set error flag
//		COM_EAT_PACKET(&txBuf);//discard the packet
//		txState = TX_IDLE_STATE;//move to the "idle" state
//		goto txIdleState;//start another packet
//sendDone:
//		return;
//	}

//Win32 Serial support: ADB 06/23/07
//RX Thread Proc
DWORD WINAPI RxThreadProc(LPVOID lpParameter)
{
	//DEBUG_STREAM<<"RX Thread Running!\n"<<std::flush;
	PCSerialPacket* pcPack = (PCSerialPacket*)lpParameter;
	//DEBUG_STREAM<<"RX Max: "<<(unsigned int)pcPack->rxMaximum<<"\n";
	//initialize com port event
	OVERLAPPED o;
	o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    o.Internal = 0;
    o.InternalHigh = 0;
    o.Offset = 0;
    o.OffsetHigh = 0;

	HANDLE events[] = {pcPack->hRxAbortEvent, o.hEvent};

	while(true)
	{
		DWORD dwEvent;
		//DEBUG_STREAM<<"Waiting for Data...\n"<<std::flush;
		WaitCommEvent(pcPack->hComm, &dwEvent, &o);
		if(WaitForMultipleObjects(2, &events[0], FALSE, INFINITE) == WAIT_OBJECT_0)//indicates that the abort event was triggered
			if(pcPack->bThreadAbort)//if we really are supposed to abort
				break;//then abort

		//perform read
		BYTE buffer[COMBUFSIZE];
		memset(&buffer, 0, COMBUFSIZE);
		DWORD bytesRead = 0;
		//DEBUG_STREAM << "Reading Data\n" << std::flush;
		ReadFile(pcPack->hComm, &buffer[0], COMBUFSIZE, NULL, &o);
		GetOverlappedResult(pcPack->hComm, &o, &bytesRead, TRUE);

		//process data
		//DEBUG_STREAM<<"Received "<<bytesRead<<" bytes! {"<<std::flush;
		for(DWORD b = 0; b < bytesRead; b++)
		{
			//if (buffer[b] == 135)
				//DEBUG_STREAM << "Received AnalogReply: " << GetTickCount() << std::endl << std::flush;
			//DEBUG_STREAM<<"0x"<<std::hex<<(unsigned int)(BYTE)buffer[b]<<std::dec<<std::flush;
			//if(b < bytesRead - 1)
			//	DEBUG_STREAM<<", ";
			//--------------------------------------------------------------
			// Process universal special characters: ACK, NAK, IDLE
			//--------------------------------------------------------------
			if(((unsigned int)buffer[b] & 0x0080) == 0)//MSB set?
				goto rcNotSpecial;//no, can't be special character
			//------------------------------
			// IDLE
			//------------------------------

			if((unsigned int)buffer[b] != IDLE_CHAR)//IDLE character?
				goto rcNotIdle;//no
			goto recvDone;//yes, ignore it
rcNotIdle:
			//------------------------------
			// ACK
			//------------------------------
			if((unsigned int)buffer[b] != ACK_CHAR)//ACK character?
				goto rcNotAck;//no
			pcPack->rxFlags |= RX_ACK_BIT;//yes, flag for transmitter
			goto recvDone;
rcNotAck:
			//------------------------------
			// NAK
			//------------------------------
			if((unsigned int)buffer[b] != NAK_CHAR)//NAK character?
				goto rcNotNak;//no
			pcPack->rxFlags |= RX_NAK_BIT;//yes, flag for transmitter
			goto recvDone;
rcNotNak:
rcNotSpecial:
			//--------------------------------------------------------------
			// Process state-dependent characters
			//--------------------------------------------------------------
			if(pcPack->rxState != 0)//test the state
				goto rxActive;//non-zero, so active
			//--------------------------------------------------------------
			// Receiver "idle" state
			// Wait for command character
			//--------------------------------------------------------------
			if(((unsigned int)buffer[b] & 0x0080) != 0)//is it a control character?
				goto rxIdleSpecial;//yes, process it

			pcPack->Error |= RX_SEQ_ERR;//Not expecting data, so error.
			goto recvDone;
rxIdleSpecial:
			if((unsigned int)buffer[b] == RESTART_CHAR)//restart?
				goto recvDone;//yes, discard it

			unsigned int bx = pcPack->COM_SPACE_IN(&pcPack->rxBuf);//get remaining space
			if(bx != 0)//zero?
				goto rxIdleSpace;//no, continue
			DEBUG_STREAM<<"RX Buffer Full!"<<std::flush;
			DISABLE_AND_DIE("RX Buffer Full!");
			pcPack->Error |= RX_COM_ERR;//error. Set flag.
			goto recvDone;
rxIdleSpace:
			unsigned int ah = (unsigned int)buffer[b];//move to ah for testing
			ah &= 0x007F;//remove MSB
			if(ah < pcPack->rxMaximum)//legitimate command?
				goto rxIdleOk;//yes, continue

			DEBUG_STREAM<<"RX Unknown Command!"<<std::flush;
			DISABLE_AND_DIE("RX Unknown Command!");
			pcPack->Error |= RX_COM_ERR;//no.
			goto recvDone;
rxIdleOk:
			pcPack->COM_PUT_AL(&pcPack->rxBuf, buffer[b]);//save command character in buffer
			pcPack->rxChecksum = buffer[b];//start a new receive checksum

			pcPack->rxCount = pcPack->rxLengthPtr[ah] + 1;//save the length

			pcPack->rxState = RX_ACTIVE_STATE;//change to "active" state
			goto recvDone;

			//--------------------------------------------------------------
			// Receiver "active" state
			// Place N characters into buffer, test checksum, and reply
			//--------------------------------------------------------------
rxActive:
			if(((unsigned int)buffer[b] & 0x0080) == 0)//control character?
				goto rxActiveData;//no, therefore data

			pcPack->Error |= RX_SEQ_ERR;//yes, error. Set error flag
			pcPack->COM_RESTORE_IN(&pcPack->rxBuf);//Discard received data
			//------------------------------
			// Process control char
			//------------------------------
			if((unsigned int)buffer[b] == RESTART_CHAR)//restart character?
				goto rxActiveAbandon;//yes, abandon received data

			pcPack->txReply = NAK_CHAR;//error, tell transmitter to send NAK
			goto rxActiveReply;
			//------------------------------
			// Process data
			//------------------------------
rxActiveData:
			pcPack->rxCount--;//check the receive count
			if(pcPack->rxCount <= 0)
				goto rxActiveCheck;//if end of message, process checksum
			//------------------------------
			// Enough space in buffer?
			//------------------------------
			bx = pcPack->COM_SPACE_IN(&pcPack->rxBuf);//get remaining space

			if(bx != 0)//zero?
				goto rxActiveSpace;//no, continue

			DEBUG_STREAM<<"RX Buffer Full @ 2!"<<std::flush;
			DISABLE_AND_DIE("RX Buffer Full @ 2!");
			pcPack->Error |= RX_COM_ERR;//error. Set flag.
			pcPack->COM_RESTORE_IN(&pcPack->rxBuf);//Discard received data
			goto rxActiveAbandon;//give up on packet

rxActiveSpace:
			//------------------------------
			// Save data in buffer
			//------------------------------
			pcPack->rxChecksum += buffer[b];//update the checksum
			pcPack->COM_PUT_AL(&pcPack->rxBuf, buffer[b]);//save the character
			goto recvDone;
			//------------------------------
			// Test checksum
			//------------------------------
rxActiveCheck:
			unsigned int bl = pcPack->rxChecksum;//get the checksum
			bl &= 0x007F;//remove MSB
			if(bl == buffer[b])//checksum OK?
				goto rxActiveOk;//yes!
			//------------------------------
			// Buffer error
			//------------------------------
			pcPack->Error |= RX_BAD_CHECKSUM;//error. Set flag.
			pcPack->COM_RESTORE_IN(&pcPack->rxBuf);//Discard received data
			pcPack->txReply = NAK_CHAR;//tell transmitter to send NAK
			TransmitCommChar(pcPack->hComm, (char)NAK_CHAR);
			goto rxActiveReply;
			//------------------------------
			// Buffer OK
			//------------------------------
rxActiveOk:
			pcPack->COM_ACCEPT_IN(&pcPack->rxBuf);//accept the received data
			pcPack->txReply = ACK_CHAR;//tell transmitter to send ACK
			TransmitCommChar(pcPack->hComm, (char)ACK_CHAR);
rxActiveReply:
			//call	StartSending				;restart transmitter if needed
			//------------------------------
			// Return to inactive state
			//------------------------------
rxActiveAbandon:
			pcPack->rxState = RX_IDLE_STATE;
recvDone:
			;
		}
		//DEBUG_STREAM<<"}\n"<<std::flush;
	}

	CloseHandle(o.hEvent);
	DEBUG_STREAM<<"Exiting RX Thread!\n"<<std::flush;
	return 0;
}
