/////////////////////////////////////////////////////////////////////////////
// DebugThread.cpp
//

#include "StdAfx.h"
#include "DxbKme.h"
#include "DebugThread.h"
#include "net.h"
#include <io.h>

#define CHECK_CANCEL	{if (WAIT_OBJECT_0 == WaitForSingleObject(pInfo->hShutdownEvent,0)){Error = "Canceled";	goto ErrorExit;}}

#define NT_KMED

#ifdef NT_KMED
#define RW_IDLE         0       /* Operation complete                   */
#define RW_READ         1       /* Read Concentrator Memory             */
#define RW_WRITE        2       /* Write Concentrator Memory            */

struct KMED_RW {
        unsigned char   rw_req;         /* Request type                 */
        unsigned char   rw_board;       /* Host Adapter board number    */
        unsigned char   rw_conc;        /* Concentrator number          */
        unsigned char   rw_reserved;    /* Reserved for expansion       */
        unsigned long   rw_addr;        /* Address in concentrator      */
        unsigned short  rw_size;        /* Read/write request length    */
        unsigned char   rw_data[128];   /* Data to read/write           */
};

#define RW_DETECT				0
#define RW_ASIS					1
#define RW_IN_NETWORK_ORDER		2

#endif	// NT_KMED


//
//      swap routines
//
static WORD
swapshort(WORD value)
{
        value &= 0xffff;
        return ((value << 8) | (value >> 8));
}

static DWORD
swaplong (DWORD val)
{
        const DWORD mask = 0x00ff00ff;

        DWORD d = (val << 16) | (val >> 16);
        return ((d >> 8) & mask) | ((d & mask) << 8);
}


UINT DebugThread( LPVOID pParam )
{
	// Pray that pParam points to one of these...
	DEBUG_INFO_T * pInfo = (DEBUG_INFO_T *)pParam;
	CString Error;
	CDxbKme DxbKme;
	SOCKET socket;
	BOOLEAN	fSwap;

	CHECK_CANCEL;

	*pInfo->pMessage = "Connecting to Dxb Driver...";
	// The board's debug queue
	if (!DxbKme.Init(pInfo->DriverName))
	{
		Error = "Couldn't initialize Dxb";
		goto ErrorExit;
	}

	CHECK_CANCEL;

	*pInfo->pMessage = "Listening...";
	do
	{
		socket = OpenSocket(pInfo->Port);
		if (-1 == socket)
		{
			Error = "Open Socket failed, open socket error...";
			goto ErrorExit;
		}
		CHECK_CANCEL;
	}while(0 == socket);

	*pInfo->pMessage = "Listening for data...";

	CHECK_CANCEL;

	{
#define DATA_BUFFER_SIZE 1024
		char DbgBuffer[DATA_BUFFER_SIZE];
#ifdef NT_KMED
		KMED_RW	rwScktBuffer;
#else
		char ScktBuffer[DATA_BUFFER_SIZE];
#endif
		int cbInDbgBuffer;
		int cbInScktBuffer;
		int cbLeft;
		int cbThisRequest;
		fd_set FdSet;
		timeval t;
		t.tv_sec = 0;//seconds
		t.tv_usec = 100000;// microseconds 1/10S
		for (;;)
		{
			FdSet.fd_count = 1;
			FdSet.fd_array[0] = socket;
			struct sockaddr clientAddr;
			int clientLen;

			if ( 1 == select(1, &FdSet, NULL, NULL, &t))
			{
#define NO_FLAGS 0
#ifdef NT_KMED
				clientLen = sizeof(clientAddr);
				cbInScktBuffer = recvfrom(socket, (char *)&rwScktBuffer, sizeof(KMED_RW), NO_FLAGS,
										  &clientAddr, &clientLen);
#else
				cbInScktBuffer = recv(socket, &ScktBuffer[0], DATA_BUFFER_SIZE, NO_FLAGS);
#endif	// NT_KMED
				if (SOCKET_ERROR == cbInScktBuffer)
				{
					Error = "Error reading from socket";
					goto ErrorExit;
				}
				if (0 < cbInScktBuffer)
				{
#ifdef REMOVED
					ASSERT(DATA_BUFFER_SIZE > cbInScktBuffer);
					ScktBuffer[cbInScktBuffer] = 0;
					Error.Format("ScktGot:%s\n", &ScktBuffer[0]);
					TRACE(Error);
#endif // #ifdef REMOVED
					*pInfo->pMessage = "Client Data";
#ifdef NT_KMED
                //
				// Convert request to host byte order (hopefully)
                //
                switch (pInfo->nEndianIndex)
                {
                case RW_ASIS:
                        break;
                case RW_IN_NETWORK_ORDER:
                        rwScktBuffer.rw_addr = ntohl(rwScktBuffer.rw_addr);
                        rwScktBuffer.rw_size = ntohs(rwScktBuffer.rw_size);
                        break;
                default:
                case RW_DETECT:
                        if (rwScktBuffer.rw_size & 0xff00)
                        {
                                rwScktBuffer.rw_size = swapshort(rwScktBuffer.rw_size);
                                rwScktBuffer.rw_addr = swaplong(rwScktBuffer.rw_addr);
                                fSwap = TRUE;
                        }
                        else
                        {
                                fSwap = FALSE;
                        }
                        break;
                }
                //
                //      Process the request
                //
                switch (rwScktBuffer.rw_req)
                {
                case RW_READ:
                        DxbKme.ReadData(/*rwScktBuffer.rw_adapter,*/ 
										rwScktBuffer.rw_addr, 
										rwScktBuffer.rw_data,
                                        rwScktBuffer.rw_size);
                        break;
                case RW_WRITE:
                        DxbKme.WriteData(/*rwScktBuffer.rw_adapter,*/ 
										rwScktBuffer.rw_addr, 
										rwScktBuffer.rw_data,
                                        rwScktBuffer.rw_size);
                        break;
                default:
                        continue;
                }

                //
                //      Convert request back to proper byte order (hopefully)
                //
                switch (pInfo->nEndianIndex)
                {
                case RW_ASIS:
                        break;
                case RW_IN_NETWORK_ORDER:
                        rwScktBuffer.rw_addr = ntohl(rwScktBuffer.rw_addr);
                        rwScktBuffer.rw_size = ntohs(rwScktBuffer.rw_size);
                        break;
                default:
                case RW_DETECT:
                        if (fSwap)
                        {
                                rwScktBuffer.rw_size = swapshort(rwScktBuffer.rw_size);
                                rwScktBuffer.rw_addr = swaplong(rwScktBuffer.rw_addr);
                        }
                        break;
                }

                //
                //      Send the reply
                //
#ifdef NT_KMED
				cbThisRequest = sendto(socket, (char *)&rwScktBuffer, cbInScktBuffer, NO_FLAGS,
										&clientAddr, clientLen);
#else
				cbThisRequest = send(socket, (char *)&rwScktBuffer, cbInScktBuffer, NO_FLAGS);

#endif	// NT_KMED
				if (SOCKET_ERROR == cbThisRequest)
				{
					Error = "Error writing debugger kme (socket)";
					goto ErrorExit;
				}
#else
					cbLeft = cbInScktBuffer;
					do
					{
						CHECK_CANCEL;
						cbThisRequest = Queue.WriteData(&ScktBuffer[cbInScktBuffer-cbLeft], cbLeft);
						if (-1 == cbThisRequest)
						{
							Error = "Error writing debugee queue (kme)";
							goto ErrorExit;
						}
						cbLeft -= cbThisRequest;

//REMOVED			if (0<cbLeft){TRACE("ScktBuffer Split\n");}

					}while(0 < cbLeft);
#endif	// NT_KMED
				}
			}

			CHECK_CANCEL;

#ifndef NT_KMED
			cbInDbgBuffer = Queue.ReadData(DbgBuffer, DATA_BUFFER_SIZE);
			if (0 < cbInDbgBuffer)
			{
#ifdef REMOVED
				ASSERT(DATA_BUFFER_SIZE > cbInDbgBuffer);
				DbgBuffer[cbInDbgBuffer] = 0;
				Error.Format("DbgGot:%s\n", &DbgBuffer[0]);
				TRACE(Error);
#endif // #ifdef REMOVED

				*pInfo->pMessage = "Target Data";
				cbLeft = cbInDbgBuffer;
				do
				{
					CHECK_CANCEL;
					cbThisRequest = send(socket, &DbgBuffer[cbInDbgBuffer - cbLeft], cbLeft, NO_FLAGS);
					if (SOCKET_ERROR == cbThisRequest)
					{
						Error = "Error writing debugger queue (socket)";
						goto ErrorExit;
					}
					cbLeft -= cbThisRequest;

//REMOVED		if (0<cbLeft){TRACE("DbgBuffer Split\n");}

				}while(0 < cbLeft);
			}
			CHECK_CANCEL;
#endif	// NT_KMED
		}
	}

	Error = "Thread Exiting";
ErrorExit:
	CloseSocket(socket);
	TRACE(Error + "\n");
	*pInfo->pMessage = Error;
	SetEvent(pInfo->hThreadCompleteEvent);
	return 0;
}
