#include "StdAfx.h"
#include "ClientManager.h"
#include "P4BridgeClient.h"
extern CRITICAL_SECTION CriticalSection; 
void MyEnterCriticalSection(CRITICAL_SECTION *CriticalSection);
ClientManager::ClientManager(void)
{
	static P4BridgeClient* pFirstItem;
	static P4BridgeClient* pLastItem;
	defaultUI = NULL;
	ExceptionError = NULL;
	pTextResultsCallbackFn = NULL;
	pInfoResultsCallbackFn = NULL;
	pTaggedOutputCallbackFn = NULL;
	pErrorCallbackFn = NULL;
	pBinaryResultsCallbackFn = NULL;
	pPromptCallbackFn = NULL;
	pResolveCallbackFn = NULL;
	pResolveACallbackFn = NULL;
}
ClientManager::~ClientManager(void)
{
	if (disposed != 0)
	{
		return;
	}
	// defaultUI is deleted when the uderlying list is deleted
	//if (defaultUI != NULL)
	//{
	//	delete defaultUI;
	//	defaultUI = NULL;
	//}
}
P4BridgeClient* ClientManager::CreateNewUI(int cmdId)
{
	MyEnterCriticalSection(&CriticalSection); 
	P4BridgeClient* newClient = new P4BridgeClient(this, cmdId);
	LeaveCriticalSection(&CriticalSection);
	return newClient;
}
P4BridgeClient* ClientManager::GetUI(int cmdId)
{
	MyEnterCriticalSection(&CriticalSection); 
	P4BridgeClient* ui = (P4BridgeClient*) Find(cmdId);
	if (ui == NULL)
	{
		ui = CreateNewUI(cmdId);
	}
	LeaveCriticalSection(&CriticalSection);
	return ui;
}
P4BridgeClient* ClientManager::GetDefaultUI()
{
	MyEnterCriticalSection(&CriticalSection); 
	if (defaultUI == NULL)
	{
		defaultUI = new P4BridgeClient(this,0);
	}
	LeaveCriticalSection(&CriticalSection);
	return defaultUI;
}
void ClientManager::ReleaseUI(int cmdId)
{
	Remove(cmdId);
}
/*******************************************************************************
 *
 *  HandleException
 *
 *  Handle any platform exceptions. The Microsoft Structured Exception Handler
 *      allows software to catch platform exceptions such as array overrun. The
 *      exception is logged, but the application will continue to run.
 *
 ******************************************************************************/
int ClientManager::HandleException(unsigned int c, struct _EXCEPTION_POINTERS *e)
{
	unsigned int code = c;
	struct _EXCEPTION_POINTERS *ep = e;
	// Log the exception
	char * exType = "Unknown";
	switch (code)
	{
	case EXCEPTION_ACCESS_VIOLATION:
		exType = "EXCEPTION_ACCESS_VIOLATION\r\n";
		break;
	case EXCEPTION_DATATYPE_MISALIGNMENT:
		exType = "EXCEPTION_DATATYPE_MISALIGNMENT\r\n";
		break;
	case EXCEPTION_BREAKPOINT:
		exType = "EXCEPTION_BREAKPOINT\r\n";
		break;
	case EXCEPTION_SINGLE_STEP:
		exType = "EXCEPTION_SINGLE_STEP\r\n";
		break;
	case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
		exType = "EXCEPTION_ARRAY_BOUNDS_EXCEEDED\r\n";
		break;
	case EXCEPTION_FLT_DENORMAL_OPERAND:
		exType = "EXCEPTION_FLT_DENORMAL_OPERAND\r\n";
		break;
	case EXCEPTION_FLT_DIVIDE_BY_ZERO:
		exType = "EXCEPTION_FLT_DIVIDE_BY_ZERO\r\n";
		break;
	case EXCEPTION_FLT_INEXACT_RESULT:
		exType = "EXCEPTION_FLT_INEXACT_RESULT\r\n";
		break;
	case EXCEPTION_FLT_INVALID_OPERATION:
		exType = "EXCEPTION_FLT_INVALID_OPERATION\r\n";
		break;
	case EXCEPTION_FLT_OVERFLOW:
		exType = "EXCEPTION_FLT_OVERFLOW\r\n";
		break;
	case EXCEPTION_FLT_STACK_CHECK:
		exType = "EXCEPTION_FLT_STACK_CHECK\r\n";
		break;
	case EXCEPTION_FLT_UNDERFLOW:
		exType = "EXCEPTION_FLT_UNDERFLOW\r\n";
		break;
	case EXCEPTION_INT_DIVIDE_BY_ZERO:
		exType = "EXCEPTION_INT_DIVIDE_BY_ZERO\r\n";
		break;
	case EXCEPTION_INT_OVERFLOW:
		exType = "EXCEPTION_INT_OVERFLOW\r\n";
		break;
	case EXCEPTION_PRIV_INSTRUCTION:
		exType = "EXCEPTION_PRIV_INSTRUCTION\r\n";
		break;
	case EXCEPTION_IN_PAGE_ERROR:
		exType = "EXCEPTION_IN_PAGE_ERROR\r\n";
		break;
	case EXCEPTION_ILLEGAL_INSTRUCTION:
		exType = "EXCEPTION_ILLEGAL_INSTRUCTION\r\n";
		break;
	case EXCEPTION_NONCONTINUABLE_EXCEPTION:
		exType = "EXCEPTION_NONCONTINUABLE_EXCEPTION\r\n";
		break;
	case EXCEPTION_STACK_OVERFLOW:
		exType = "EXCEPTION_STACK_OVERFLOW\r\n";
		break;
	case EXCEPTION_INVALID_DISPOSITION:
		exType = "EXCEPTION_INVALID_DISPOSITION\r\n";
		break;
	case EXCEPTION_GUARD_PAGE:
		exType = "EXCEPTION_GUARD_PAGE\r\n";
		break;
	case EXCEPTION_INVALID_HANDLE:
		exType = "EXCEPTION_INVALID_HANDLE\r\n";
		break;
	//case EXCEPTION_POSSIBLE_DEADLOCK:
	//    exType = "EXCEPTION_POSSIBLE_DEADLOCK\r\n");
	//    break;
	default:
		printf("UNKOWN EXCEPTION\r\n");
		break;
	}
	if (ExceptionError != NULL)
		delete ExceptionError;
		
	ExceptionError = new StrBuf();
	ExceptionError->Append("Exception Detected in callback function: ");
	ExceptionError->Append(exType);
	return EXCEPTION_EXECUTE_HANDLER;
}
/*******************************************************************************
 *
 *  CallTextResultsCallbackFn
 *
 *  Simple wrapper to call the callback function (if it has been set) within a
 *      SEH __try block to catch any platform exception. SEH __try blocks must
 *      be contained in simple functions or you will get Compiler Error C2712,
 *      "cannot use __try in functions that require object unwinding"
 *
 ******************************************************************************/
void ClientManager::CallTextResultsCallbackFn(int cmdId, const char *data)
{
	__try
	{
		if (pTextResultsCallbackFn != NULL)
		{
			(*pTextResultsCallbackFn)( cmdId, data );
		}
	}  __except (HandleException(GetExceptionCode(), GetExceptionInformation()))
	{
		this->GetUI(cmdId)->HandleError( E_FATAL, 0, ExceptionError->Text());
	}
}
/*******************************************************************************
 *
 *  CallInfoResultsCallbackFn
 *
 *  Simple wrapper to call the callback function (if it has been set) within a
 *      SEH __try block to catch any platform exception. SEH __try blocks must
 *      be contained in simple functions or you will get Compiler Error C2712,
 *      "cannot use __try in functions that require object unwinding"
 *
 ******************************************************************************/
void ClientManager::CallInfoResultsCallbackFn( int cmdId, char level, const char *data )
{
	__try
	{
		if 	(pInfoResultsCallbackFn != NULL)
		{
			int nlevel = (int)(level - '0');
			(*pInfoResultsCallbackFn)( cmdId, nlevel, data );
		}
	}
	__except (HandleException(GetExceptionCode(), GetExceptionInformation()))
	{
		GetUI(cmdId)->HandleError( E_FATAL, 0, ExceptionError->Text());
	}
}
/*******************************************************************************
 *
 *  CallTaggedOutputCallbackFn
 *
 *  Simple wrapper to call the callback function (if it has been set) within a
 *      SEH __try block to catch any platform exception. SEH __try blocks must
 *      be contained in simple functions or you will get Compiler Error C2712,
 *      "cannot use __try in functions that require object unwinding"
 *
 ******************************************************************************/
void ClientManager::CallTaggedOutputCallbackFn( int cmdId, int objId, const char *pKey, const char * pVal )
{
	__try
	{
		if (pTaggedOutputCallbackFn != NULL)
		{
			(*pTaggedOutputCallbackFn)( cmdId, objId, pKey, pVal );
		}
	}
	__except (HandleException(GetExceptionCode(), GetExceptionInformation()))
	{
		GetUI(cmdId)->HandleError( E_FATAL, 0, ExceptionError->Text());
	}
}
/*******************************************************************************
 *
 *  CallErrorCallbackFn
 *
 *  Simple wrapper to call the callback function (if it has been set) within a
 *      SEH __try block to catch any platform exception. SEH __try blocks must
 *      be contained in simple functions or you will get Compiler Error C2712,
 *      "cannot use __try in functions that require object unwinding"
 *
 ******************************************************************************/
void ClientManager::CallErrorCallbackFn( int cmdId, int severity, int errorId, const char * errMsg )
{
	__try
	{
		if 	(pErrorCallbackFn != NULL)
		{
			(*pErrorCallbackFn)( cmdId, severity, errorId, errMsg );
		}
	}
	__except (HandleException(GetExceptionCode(), GetExceptionInformation()))
	{
		// could cause infinite recursion if we keep producing errors 
		//  when reporting errors
		pErrorCallbackFn = NULL;
		GetUI(cmdId)->HandleError( E_FATAL, 0, ExceptionError->Text());
	}
}
/*******************************************************************************
 *
 *  CallErrorCallbackFn
 *
 *  Simple wrapper to call the callback function (if it has been set) within a
 *      SEH __try block to catch any platform exception. SEH __try blocks must
 *      be contained in simple functions or you will get Compiler Error C2712,
 *      "cannot use __try in functions that require object unwinding"
 *
 ******************************************************************************/
void ClientManager::CallBinaryResultsCallbackFn( int cmdId, void * data, int length )
{
	__try
	{
		if (pBinaryResultsCallbackFn)
			(*pBinaryResultsCallbackFn)( cmdId, (void *) data, length );
	}
	__except (HandleException(GetExceptionCode(), GetExceptionInformation()))
	{
		GetUI(cmdId)->HandleError( E_FATAL, 0, ExceptionError->Text());
	}
}
// Set the call back function to receive the tagged output
void ClientManager::SetTaggedOutputCallbackFn(IntTextTextCallbackFn* pNew)
{
	pTaggedOutputCallbackFn = pNew;
}
// Set the call back function to receive the error output
void ClientManager::SetErrorCallbackFn(IntIntIntTextCallbackFn* pNew)
{
	pErrorCallbackFn = pNew;
}
void ClientManager::Prompt( int cmdId, const StrPtr &msg, StrBuf &rsp, 
			int noEcho, Error *e )
{
	__try
	{
		if (pPromptCallbackFn)
		{
			char response[1024];
			(*pPromptCallbackFn)( cmdId, msg.Text(), response, sizeof(response), noEcho);
			rsp.Set(response);
		}
	}  __except (HandleException(GetExceptionCode(), GetExceptionInformation()))
	{
		GetUI(cmdId)->HandleError( E_FATAL, 0, ExceptionError->Text());
	}
}
void ClientManager::SetPromptCallbackFn( PromptCallbackFn * pNew)
{
	pPromptCallbackFn = pNew;
}
// Set the call back function to receive the information output
void ClientManager::SetInfoResultsCallbackFn(IntIntTextCallbackFn* pNew)
{
	pInfoResultsCallbackFn = pNew;
}
// Set the call back function to receive the text output
void ClientManager::SetTextResultsCallbackFn(TextCallbackFn* pNew)
{
	pTextResultsCallbackFn = pNew;
}
// Set the call back function to receive the binary output
void ClientManager::SetBinaryResultsCallbackFn(BinaryCallbackFn* pNew)
{
	pBinaryResultsCallbackFn = pNew;
}
// Callbacks for handling interactive resolve
int	ClientManager::Resolve( int cmdId, ClientMerge *m, Error *e )
{
	if (pResolveCallbackFn == NULL)
	{
		return CMS_SKIP;
	}
	P4ClientMerge *merger = new P4ClientMerge(m);
	int result = -1;
	result = Resolve_int( cmdId, merger );
	delete merger;
	if (result == -1)
	{
		return GetUI(cmdId)->ClientUser::Resolve( m, e );
	}
	return result;
}
int	ClientManager::Resolve( int cmdId, ClientResolveA *r, int preview, Error *e )
{
	if (pResolveACallbackFn == NULL)
	{
		return CMS_SKIP;
	}
	P4ClientResolve *resolver = new P4ClientResolve(r, isUnicode);
	int result = -1;
	result = Resolve_int( cmdId, resolver, preview, e);
	delete resolver;
	if (result == -1)
	{
		return CMS_SKIP;
	}
	return result;
}
void ClientManager::SetResolveCallbackFn(ResolveCallbackFn * pNew)
{
	pResolveCallbackFn = pNew;
}
void ClientManager::SetResolveACallbackFn(ResolveACallbackFn * pNew)
{
	pResolveACallbackFn = pNew;
}
int ClientManager::Resolve_int( int cmdId, P4ClientMerge *merger)
{
	int result = -1;
	__try
	{
		 result = (*pResolveCallbackFn)(cmdId, merger);
	}  
	__except (HandleException(GetExceptionCode(), GetExceptionInformation()))
	{
		GetUI(cmdId)->HandleError( E_FATAL, 0, ExceptionError->Text());
	}
	return result;
}
int ClientManager::Resolve_int( int cmdId, P4ClientResolve *resolver, int preview, Error *e)
{
	int result = -1;
	__try
	{
		result = (*pResolveACallbackFn)(cmdId, resolver, preview);
	}  
	__except (HandleException(GetExceptionCode(), GetExceptionInformation()))
	{
		GetUI(cmdId)->HandleError( E_FATAL, 0, ExceptionError->Text());
	}
	return result;
}