#include "hdr/modern_commonheaders.h"
#include "hdr/modern_sync.h"

typedef struct tagSYNCCALLITEM
{
	WPARAM  wParam;
	LPARAM  lParam;
	int     nResult;
	HANDLE  hDoneEvent;
	PSYNCCALLBACKPROC pfnProc;    
} SYNCCALLITEM;
static void CALLBACK _SyncCallerUserAPCProc(ULONG_PTR dwParam)
{
	SYNCCALLITEM* item = (SYNCCALLITEM*) dwParam;
	item->nResult = item->pfnProc(item->wParam, item->lParam);
	SetEvent(item->hDoneEvent);
}
static INT_PTR SyncCaller(WPARAM proc, LPARAM lParam)
{
	typedef int (*P0PARAMFUNC)();
	typedef int (*P1PARAMFUNC)(WPARAM);
	typedef int (*P2PARAMFUNC)(WPARAM, LPARAM);
	typedef int (*P3PARAMFUNC)(WPARAM, LPARAM, LPARAM);
	typedef int (*P4PARAMFUNC)(WPARAM, LPARAM, LPARAM, LPARAM);

	LPARAM * params=(LPARAM *)lParam;
	int count=params[0];
	switch (count)
	{
	case 0:			
		{
			P0PARAMFUNC pfnProc=(P0PARAMFUNC)proc;
			return pfnProc();
		}
	case 1:
		{
			P1PARAMFUNC pfnProc=(P1PARAMFUNC)proc;
			return pfnProc((WPARAM)params[1]);
		}
	case 2:
		{
			P2PARAMFUNC pfnProc=(P2PARAMFUNC)proc;
			return pfnProc((WPARAM)params[1],(LPARAM)params[2]);
		}
	case 3:
		{
			P3PARAMFUNC pfnProc=(P3PARAMFUNC)proc;
			return pfnProc((WPARAM)params[1],(LPARAM)params[2], (LPARAM)params[3]);
		}
	case 4:
		{
			P4PARAMFUNC pfnProc=(P4PARAMFUNC)proc;
			return pfnProc((WPARAM)params[1],(LPARAM)params[2], (LPARAM)params[3], (LPARAM)params[4]);
		}
	}
	return 0;
}


int SyncCall(void * vproc, int count, ... )
{
	LPARAM params[5];
	va_list va;
	int i;
	params[0]=(LPARAM)count;
	va_start(va, count);
	for (i=0; i<count && i<SIZEOF(params)-1; i++)
	{
		params[i+1]=va_arg(va,LPARAM);
	}
	va_end(va);
	return SyncCallProxy(SyncCaller, (WPARAM)vproc, (LPARAM) params);
}

int SyncCallProxy(PSYNCCALLBACKPROC pfnProc, WPARAM wParam, LPARAM lParam, CRITICAL_SECTION * cs /* = NULL */)
{  
	SYNCCALLITEM item={0};
	
	int nReturn = 0;

	if ( cs != NULL )
	{
		if ( !fnTryEnterCriticalSection ) // for poor OSes like Win98
		{
			EnterCriticalSection( cs );
			int result = pfnProc( wParam, lParam );
			LeaveCriticalSection( cs );
			return result;
		}

		if ( fnTryEnterCriticalSection( cs ))
		{   //simple call (Fastest)
			int result=pfnProc(wParam,lParam);
			LeaveCriticalSection( cs );
			return result;
		}
		else
		{	//Window SendMessage Call(Middle)
			if ( SyncCallWinProcProxy( pfnProc, wParam, lParam, nReturn ) == S_OK)
				return nReturn;
		}
	}
	if ( SyncCallAPCProxy( pfnProc, wParam, lParam, nReturn ) == S_OK)
		return nReturn;
	
	return NULL;
}

HRESULT SyncCallWinProcProxy( PSYNCCALLBACKPROC pfnProc, WPARAM wParam, LPARAM lParam, int& nReturn )
{
	nReturn = 0;
	if (pcli->hwndContactList == NULL )
		return E_FAIL;
	
	SYNCCALLITEM item={0};
	item.wParam = wParam;
	item.lParam = lParam;
	item.pfnProc = pfnProc;
	nReturn = SendMessage(pcli->hwndContactList, UM_SYNCCALL, (WPARAM)&item,0);
	
	return S_OK;
}

HRESULT SyncCallAPCProxy( PSYNCCALLBACKPROC pfnProc, WPARAM wParam, LPARAM lParam, int& hReturn )
{
	hReturn = 0;

	if (g_hMainThread==NULL || pfnProc==NULL) 
		return E_FAIL;

	SYNCCALLITEM item={0};

	if (GetCurrentThreadId()!=g_dwMainThreadID)
	{
		item.wParam = wParam;
		item.lParam = lParam;
		item.pfnProc = pfnProc;
		item.hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

		QueueUserAPC(_SyncCallerUserAPCProc, g_hMainThread, (ULONG_PTR) &item);	
		PostMessage(pcli->hwndContactList,WM_NULL,0,0); // let this get processed in its own time
		
		WaitForSingleObject(item.hDoneEvent, INFINITE);
		CloseHandle(item.hDoneEvent);

		hReturn = item.nResult;

		return S_OK;
	}
	/* else */

	hReturn = pfnProc(wParam, lParam);
	return S_OK;
}





LRESULT SyncOnWndProcCall( WPARAM wParam )
{
	SYNCCALLITEM *psci=(SYNCCALLITEM *)wParam;
	if (psci)
		return psci->pfnProc( psci->wParam, psci->lParam );
	return 0;
}

int DoCall( PSYNCCALLBACKPROC pfnProc, WPARAM wParam, LPARAM lParam )
{
	return SyncCallProxy( pfnProc, 0, lParam );
}