#include "loader.h"
#include "context.h"

AGENT_CTX AgentContext = {0};

/*
 *
 */
VOID context_init( VOID )
{
	memset( &AgentContext, 0, sizeof(AGENT_CTX) );

	AgentContext.bDisableCourtesyShell = FALSE;
	AgentContext.bInit                 = TRUE;
	AgentContext.hCloseEvent           = NULL;
	AgentContext.dwEncoding            = 0;
	AgentContext.dwCompressLevel       = 6;
	AgentContext.dwQualityLevel        = -1;
	AgentContext.bUseCopyRect          = FALSE;
	AgentContext.bEncodingRichCursor   = FALSE;
	AgentContext.bEncodingPointerPos   = FALSE;
	AgentContext.bEncodingLastRect     = FALSE;
	AgentContext.bEncodingNewfbSize    = FALSE;
	AgentContext.bEncodingXCursor      = FALSE;

	/*AgentContext.dictionaries[0]       = NULL;
	AgentContext.dictionaries[1]       = NULL;
	AgentContext.dictionaries[2]       = NULL;
	AgentContext.dictionaries[3]       = NULL;*/

	AgentContext.dwPipeName            = ( GetTickCount() ^ (DWORD)&AgentContext );
}

/*
 * Try to read an exact ammount of data from a pipe and return 
 * when either the data has been read or a failure occurs.
 */
DWORD _readexact( HANDLE hPipe, DWORD dwLength, BYTE * pBuffer )
{
	DWORD dwTotal = 0;
	DWORD dwRead  = 0;

	do
	{
		while( dwTotal < dwLength )
		{
			if( !PeekNamedPipe( hPipe, NULL, 0, NULL, &dwRead, NULL ) )
				break;

			if( !dwRead )
			{
				Sleep( 50 );
				continue;
			}

			if( ReadFile( hPipe, (LPVOID)((LPBYTE)pBuffer + dwTotal), (dwLength - dwTotal), &dwRead, NULL ) )
				dwTotal += dwRead;
		}

	} while( 0 );

	return dwTotal;
}

/*
 * A thread to pick up any messages being posted back to the loader (such as an encoder change in the stream)
 */
DWORD WINAPI context_message_thread( LPVOID lpParameter )
{
	DWORD dwResult            = ERROR_SUCCESS;
	HANDLE hServerPipe        = NULL;
	BYTE * pBuffer            = NULL;
	char cNamedPipe[MAX_PATH] = {0};

	__try
	{
		do
		{
			_snprintf( cNamedPipe, MAX_PATH, "\\\\.\\pipe\\%08X", AgentContext.dwPipeName );

			dprintf("[LOADER] loader_message_thread. cNamedPipe=%s", cNamedPipe );
				
			hServerPipe = CreateNamedPipe( cNamedPipe, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 0, 0, NULL );
			if( !hServerPipe )
				BREAK_ON_ERROR( "[LOADER] loader_message_thread. CreateNamedPipe failed" );
				
			while( TRUE )
			{
				struct _hdr {
					DWORD dwMessage;
					DWORD dwLength;
				} header = {0};
				DWORD dwTotal = 0;

				if( !ConnectNamedPipe( hServerPipe, NULL ) )
				{
					if( GetLastError() != ERROR_PIPE_CONNECTED )
						continue;
				}

				dwTotal = _readexact( hServerPipe, 8, (BYTE *)&header );
				if( dwTotal != sizeof( struct _hdr ) )
					BREAK_WITH_ERROR( "[LOADER] loader_message_thread. _readexact header failed", ERROR_INVALID_HANDLE );

				pBuffer = (BYTE *)malloc( header.dwLength );
				if( !pBuffer )
					BREAK_WITH_ERROR( "[LOADER] loader_message_thread. pBuffer malloc failed", ERROR_INVALID_HANDLE );
				
				dwTotal = _readexact( hServerPipe, header.dwLength, pBuffer );
				if( dwTotal != header.dwLength )
					BREAK_WITH_ERROR( "[LOADER] loader_message_thread. _readexact pBuffer failed", ERROR_INVALID_HANDLE );

				DisconnectNamedPipe( hServerPipe );

				switch( header.dwMessage )
				{
					case MESSAGE_SETENCODING:
						if( header.dwLength != sizeof(DWORD) )
						{
							dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODING, not enought data (got %d bytes)", header.dwLength );
							break;
						}
						AgentContext.dwEncoding = *(DWORD *)pBuffer;
						dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODING, new encoding is %d", AgentContext.dwEncoding );
						break;
					case MESSAGE_SETPIXELFORMAT:
						if( header.dwLength != sizeof(PIXELFORMAT) )
						{
							dprintf("[LOADER] loader_message_thread. MESSAGE_SETPIXELFORMAT, not enought data (got %d bytes)", header.dwLength );
							break;
						}
						memcpy( &AgentContext.PixelFormat, pBuffer, sizeof(PIXELFORMAT) );
						dprintf("[LOADER] loader_message_thread. MESSAGE_SETPIXELFORMAT" );
						break;
					case MESSAGE_SETCOMPRESSLEVEL:
						if( header.dwLength != sizeof(DWORD) )
						{
							dprintf("[LOADER] loader_message_thread. MESSAGE_SETCOMPRESSLEVEL, not enought data (got %d bytes)", header.dwLength );
							break;
						}
						AgentContext.dwCompressLevel = *(DWORD *)pBuffer;
						dprintf("[LOADER] loader_message_thread. MESSAGE_SETCOMPRESSLEVEL, new compress level is %d", AgentContext.dwCompressLevel );
						break;
					case MESSAGE_SETQUALITYLEVEL:
						if( header.dwLength != sizeof(DWORD) )
						{
							dprintf("[LOADER] loader_message_thread. MESSAGE_SETQUALITYLEVEL, not enought data (got %d bytes)", header.dwLength );
							break;
						}
						AgentContext.dwQualityLevel = *(DWORD *)pBuffer;
						dprintf("[LOADER] loader_message_thread. MESSAGE_SETQUALITYLEVEL, new quality level is %d", AgentContext.dwQualityLevel );
						break;
					case MESSAGE_SETCOPYRECTUSE:
						if( header.dwLength != sizeof(BOOL) )
						{
							dprintf("[LOADER] loader_message_thread. MESSAGE_SETCOPYRECTUSE, not enought data (got %d bytes)", header.dwLength );
							break;
						}
						AgentContext.bUseCopyRect = *(BOOL *)pBuffer;
						dprintf("[LOADER] loader_message_thread. MESSAGE_SETCOPYRECTUSE, new bUseCopyRect is %d", AgentContext.bUseCopyRect );
						break;
					case MESSAGE_SETENCODINGRICHCURSOR:
						if( header.dwLength != sizeof(BOOL) )
						{
							dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGRICHCURSOR, not enought data (got %d bytes)", header.dwLength );
							break;
						}
						AgentContext.bEncodingRichCursor = *(BOOL *)pBuffer;
						dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGRICHCURSOR, new dwEncodingRichCursor is %d", AgentContext.bEncodingRichCursor );
						break;
					case MESSAGE_SETENCODINGPOINTERPOS:
						if( header.dwLength != sizeof(BOOL) )
						{
							dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGPOINTERPOS, not enought data (got %d bytes)", header.dwLength );
							break;
						}
						AgentContext.bEncodingPointerPos = *(BOOL *)pBuffer;
						dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGPOINTERPOS, new dwEncodingPointerPos is %d", AgentContext.bEncodingPointerPos );
						break;
					case MESSAGE_SETENCODINGLASTRECT:
						if( header.dwLength != sizeof(BOOL) )
						{
							dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGLASTRECT, not enought data (got %d bytes)", header.dwLength );
							break;
						}
						AgentContext.bEncodingLastRect = *(BOOL *)pBuffer;
						dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGLASTRECT, new dwEncodingLastRect is %d", AgentContext.bEncodingLastRect );
						break;
					case MESSAGE_SETENCODINGNEWFBSIZE:
						if( header.dwLength != sizeof(BOOL) )
						{
							dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGNEWFBSIZE, not enought data (got %d bytes)", header.dwLength );
							break;
						}
						AgentContext.bEncodingNewfbSize = *(BOOL *)pBuffer;
						dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGNEWFBSIZE, new bEncodingNewfbSize is %d", AgentContext.bEncodingNewfbSize );
						break;
					case MESSAGE_SETENCODINGXCURSOR:
						if( header.dwLength != sizeof(BOOL) )
						{
							dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGXCURSOR, not enought data (got %d bytes)", header.dwLength );
							break;
						}
						AgentContext.bEncodingXCursor = *(BOOL *)pBuffer;
						dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGXCURSOR, new bEncodingXCursor is %d", AgentContext.bEncodingXCursor );
						break;
					/*
					case MESSAGE_SETZLIBDICTIONARY:
						if( header.dwLength < sizeof(DICTMSG) )
						{
							dprintf("[LOADER] loader_message_thread. MESSAGE_SETZLIBDICTIONARY, not enought data (got %d bytes)", header.dwLength );
							break;
						}
						else
						{
							DICTMSG * dmsg = (DICTMSG *)pBuffer;
							if( dmsg->dwId > 4 )
							{
								dprintf("[LOADER] loader_message_thread. MESSAGE_SETZLIBDICTIONARY, invalid id (got %d)", dmsg->dwId );
								break;
							}

							if( AgentContext.dictionaries[dmsg->dwId] )
								free( AgentContext.dictionaries[dmsg->dwId] );

							AgentContext.dictionaries[dmsg->dwId] = (DICTMSG *)malloc( sizeof(DICTMSG) + dmsg->dwDictLength );
							if( !AgentContext.dictionaries[dmsg->dwId] )
							{
								dprintf("[LOADER] loader_message_thread. MESSAGE_SETZLIBDICTIONARY, malloc failed" );
								break;
							}						
							
							AgentContext.dictionaries[dmsg->dwId]->dwId         = dmsg->dwId;
							AgentContext.dictionaries[dmsg->dwId]->dwDictLength = dmsg->dwDictLength;

							memcpy( &AgentContext.dictionaries[dmsg->dwId]->bDictBuffer, &dmsg->bDictBuffer, dmsg->dwDictLength );

							dprintf("[LOADER] loader_message_thread. MESSAGE_SETZLIBDICTIONARY, id=%d, length=%d", dmsg->dwId, dmsg->dwDictLength );
						}
						break;
					*/
					default:
						dprintf("[LOADER] loader_message_thread. Unknown message 0x%08X", header.dwMessage );
						break;
				}

				if( pBuffer )
				{
					free( pBuffer );
					pBuffer = NULL;
				}
			}

		} while( 0 );
	}
	__except( EXCEPTION_EXECUTE_HANDLER )
	{
		dprintf( "[LOADER] loader_message_thread. EXCEPTION_EXECUTE_HANDLER\n\n" );
	}

	dprintf("[LOADER] loader_message_thread. thread finishing...");

	if( hServerPipe )
	{
		DisconnectNamedPipe( hServerPipe );
		CLOSE_HANDLE( hServerPipe );
	}

	if( pBuffer )
		free( pBuffer );

	return dwResult;
}
