/***********************************************************************
 * progress.C
 *  
 *    Progress Bar
 *
 *
 * Toby Opferman Copyright (c) 2003
 *
 ***********************************************************************/
 
 
 #include <windows.h>
 #include <Commctrl.h>
 #include <progress.h>

typedef struct _QUICK_PROGRESS_DATA
{
	PFNPROGRESSBARWORK pfnProgressBarWork;

	HANDLE hThread;
	PVOID pReturnData;

}QUICK_PROGRESS_DATA, *PQUICK_PROGRESS_DATA;

typedef struct _PROGRESS_DATA
{
	HWND hWnd;
	HINSTANCE hInstance;
	HWND hProgressWnd;
	HWND hStatusBar;
	HWND hProgressBar;
	PFNCANCELCALLBACK pfnCancelCallback;
    PVOID pContext;
	BOOL HandleIsQuickProgress;
	BOOL ClosePending;
	QUICK_PROGRESS_DATA QuickProgressData;
}PROGRESS_DATA, *PPROGRESS_DATA;

typedef struct _QUICK_GUI_CONTEXT
{
       HWND hWnd;
	   HINSTANCE hInstance;
	   UINT uiResource;
	   PVOID pContext;
	   PFNCANCELCALLBACK pfnCancelCallback;
	   PFNPROGRESSBARWORK pfnProgressBarWork;
	   HANDLE hEvent;
	   PPROGRESS_DATA pProgressData;

} QUICK_GUI_CONTEXT, *PQUICK_GUI_CONTEXT;

DWORD WINAPI Progress_QuickWorkThread(PVOID pContext);
DWORD WINAPI Progress_QuickGuiThread(PVOID pContext);
BOOL WINAPI Progress_WindowProc(HWND hWnd, UINT uiMessage, WPARAM wParam, LPARAM lParam);
PVOID Progress_QuickProgressBarCurrentThread(HWND hWnd, HINSTANCE hInstance, UINT uiResource, PVOID pContext, PFNCANCELCALLBACK pfnCancelCallback, PFNPROGRESSBARWORK pfnProgressBarWork);
PVOID Progress_QuickProgressBarTemporaryThread(HWND hWnd, HINSTANCE hInstance, UINT uiResource, PVOID pContext, PFNCANCELCALLBACK pfnCancelCallback, PFNPROGRESSBARWORK pfnProgressBarWork);

 /***********************************************************************
  * Progress_CreateDisplay
  *  
  *    
  *
  * Parameters
  *     
  * 
  * Return Value
  *     
  *
  ***********************************************************************/
HPROGRESS Progress_CreateDisplay(HWND hWnd, HINSTANCE hInstance, UINT uiResource, PVOID pContext, PFNCANCELCALLBACK pfnCancelCallback)
{
	PPROGRESS_DATA pProgressData = NULL;
	INITCOMMONCONTROLSEX InitializeCommonControls ={0};

	InitializeCommonControls.dwSize = sizeof(INITCOMMONCONTROLSEX);
	InitializeCommonControls.dwICC = ICC_PROGRESS_CLASS | ICC_BAR_CLASSES;

    InitCommonControlsEx(&InitializeCommonControls);

	pProgressData =(PPROGRESS_DATA)LocalAlloc(LMEM_ZEROINIT, sizeof(PROGRESS_DATA));

	if(pProgressData)
	{
		pProgressData->pContext          = pContext;
		pProgressData->pfnCancelCallback = pfnCancelCallback;
		pProgressData->hWnd              = hWnd;
		pProgressData->hInstance         = hInstance;
		pProgressData->hProgressWnd      = CreateDialogParam(hInstance, MAKEINTRESOURCE(uiResource), hWnd, Progress_WindowProc, (LPARAM)pProgressData);

		if(pProgressData->hProgressWnd == NULL)
		{
			LocalFree(pProgressData);
			pProgressData = NULL;
		}
		else
		{
			pProgressData->hStatusBar = GetDlgItem(pProgressData->hProgressWnd, IDC_PROGRESS_STATUS);
			pProgressData->hProgressBar = GetDlgItem(pProgressData->hProgressWnd, IDC_PROGRESS_BAR);
		}

	}

	return (HPROGRESS)pProgressData;
}


 /***********************************************************************
  * Progress_QuickProgressBar
  *  
  *    
  *
  * Parameters
  *     
  * 
  * Return Value
  *     
  *
  ***********************************************************************/
PVOID Progress_QuickProgressBar(HWND hWnd, HINSTANCE hInstance, UINT uiResource, PVOID pContext, PFNCANCELCALLBACK pfnCancelCallback, PFNPROGRESSBARWORK pfnProgressBarWork, DWORD dwFlags)
{
	PVOID pReturnValue;

	if(dwFlags & FLAG_WORKER_CONTEXT_CURRENT_THREAD)
	{
		pReturnValue = Progress_QuickProgressBarTemporaryThread(hWnd, hInstance, uiResource, pContext, pfnCancelCallback, pfnProgressBarWork);
	}
	else
	{
		pReturnValue = Progress_QuickProgressBarCurrentThread(hWnd, hInstance, uiResource, pContext, pfnCancelCallback, pfnProgressBarWork);
	}

	return pReturnValue; 
}


 /***********************************************************************
  * Progress_QuickProgressBarTemporaryThread
  *  
  *    
  *
  * Parameters
  *     
  * 
  * Return Value
  *     
  *
  ***********************************************************************/
PVOID Progress_QuickProgressBarTemporaryThread(HWND hWnd, HINSTANCE hInstance, UINT uiResource, PVOID pContext, PFNCANCELCALLBACK pfnCancelCallback, PFNPROGRESSBARWORK pfnProgressBarWork)
{
	PVOID pReturnValue = NULL;
	DWORD ThreadId;
	HANDLE hThread;
    PQUICK_GUI_CONTEXT pQuickGuiContext;

	pQuickGuiContext = (PQUICK_GUI_CONTEXT)LocalAlloc(LMEM_ZEROINIT, sizeof(QUICK_GUI_CONTEXT));

	if(pQuickGuiContext)
	{
		pQuickGuiContext->hWnd                = hWnd;
		pQuickGuiContext->hInstance           = hInstance;
		pQuickGuiContext->uiResource          = uiResource;
		pQuickGuiContext->pContext            = pContext;
		pQuickGuiContext->pfnCancelCallback   = pfnCancelCallback;
		pQuickGuiContext->pfnProgressBarWork  = pfnProgressBarWork;

		pQuickGuiContext->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

		if(pQuickGuiContext->hEvent != NULL && pQuickGuiContext->hEvent != INVALID_HANDLE_VALUE)
		{
			hThread = CreateThread(NULL, 0, Progress_QuickGuiThread, pQuickGuiContext, 0, &ThreadId);

			if(hThread != NULL && hThread != INVALID_HANDLE_VALUE)
			{
				WaitForSingleObject(pQuickGuiContext->hEvent, INFINITE);

				if(pQuickGuiContext->pProgressData)
				{
					pfnProgressBarWork((HPROGRESS)pQuickGuiContext->pProgressData, pQuickGuiContext->pProgressData->pContext);
							        
					pReturnValue = pQuickGuiContext->pProgressData->QuickProgressData.pReturnData;

					Progress_CompleteAndClose((HPROGRESS)pQuickGuiContext->pProgressData);
					WaitForSingleObject(hThread, INFINITE);
					pQuickGuiContext->pProgressData->HandleIsQuickProgress = FALSE;
					Progress_CompleteAndClose((HPROGRESS)pQuickGuiContext->pProgressData);
				}

				CloseHandle(hThread);
			}

			CloseHandle(pQuickGuiContext->hEvent);
		}

		LocalFree(pQuickGuiContext);
	}

    return pReturnValue;
}


 /***********************************************************************
  * Progress_QuickGuiThread
  *  
  *    
  *
  * Parameters
  *     
  * 
  * Return Value
  *     
  *
  ***********************************************************************/
DWORD WINAPI Progress_QuickGuiThread(PVOID pContext)
{
	PQUICK_GUI_CONTEXT pQuickGuiContext = (PQUICK_GUI_CONTEXT)pContext;

	pQuickGuiContext->pProgressData = (PPROGRESS_DATA)Progress_CreateDisplay(pQuickGuiContext->hWnd, pQuickGuiContext->hInstance, pQuickGuiContext->uiResource, pQuickGuiContext->pContext, pQuickGuiContext->pfnCancelCallback);

	if(pQuickGuiContext->pProgressData)
	{
		pQuickGuiContext->pProgressData->HandleIsQuickProgress = TRUE;
		pQuickGuiContext->pProgressData->QuickProgressData.pfnProgressBarWork = pQuickGuiContext->pfnProgressBarWork;
		SetEvent(pQuickGuiContext->hEvent);		
		Progress_MessageLoop((HPROGRESS)pQuickGuiContext->pProgressData);
	}
	else
	{
		SetEvent(pQuickGuiContext->hEvent);
	}

	return 0;
}

 /***********************************************************************
  * Progress_QuickProgressBarCurrentThread
  *  
  *    
  *
  * Parameters
  *     
  * 
  * Return Value
  *     
  *
  ***********************************************************************/
PVOID Progress_QuickProgressBarCurrentThread(HWND hWnd, HINSTANCE hInstance, UINT uiResource, PVOID pContext, PFNCANCELCALLBACK pfnCancelCallback, PFNPROGRESSBARWORK pfnProgressBarWork)
{
	PPROGRESS_DATA pProgressData;
	PVOID pReturnValue = NULL;
	DWORD ThreadId;

	pProgressData = (PPROGRESS_DATA)Progress_CreateDisplay(hWnd, hInstance, uiResource, pContext, pfnCancelCallback);

	if(pProgressData)
	{
		pProgressData->HandleIsQuickProgress = TRUE;
		pProgressData->QuickProgressData.pfnProgressBarWork = pfnProgressBarWork;

		pProgressData->QuickProgressData.hThread = CreateThread(NULL, 0, Progress_QuickWorkThread, pProgressData, 0, &ThreadId);

		if(pProgressData->QuickProgressData.hThread != NULL && pProgressData->QuickProgressData.hThread != INVALID_HANDLE_VALUE)
		{
			Progress_MessageLoop((HPROGRESS)pProgressData);
			
			WaitForSingleObject(pProgressData->QuickProgressData.hThread, INFINITE);
			CloseHandle(pProgressData->QuickProgressData.hThread);
	        
			pReturnValue = pProgressData->QuickProgressData.pReturnData;
		}

		pProgressData->HandleIsQuickProgress = FALSE;
		Progress_CompleteAndClose((HPROGRESS)pProgressData);
	}
    
    return pReturnValue;
}

 /***********************************************************************
  * Progress_MessageLoop
  *  
  *    
  *
  * Parameters
  *     
  * 
  * Return Value
  *     
  *
  ***********************************************************************/
void Progress_MessageLoop(HPROGRESS hProgress)
{
	 PPROGRESS_DATA pProgressData = (PPROGRESS_DATA)hProgress;
     MSG Msg = {0};
     BOOL bMessageLoop = TRUE;

     while(bMessageLoop)
     {
	    if(GetMessage(&Msg, 0, 0, 0) > 0)
		{
			if(IsDialogMessage(pProgressData->hProgressWnd, &Msg) == FALSE)
			{
				TranslateMessage(&Msg);
				DispatchMessage(&Msg);
			}
			else
			{
				if(IsWindow(pProgressData->hProgressWnd) == FALSE || Msg.message == 0)
				{
					bMessageLoop = FALSE;
				}
			}
		}
		else
		{
			bMessageLoop = FALSE;
		}
     }
}


 /***********************************************************************
  * Progress_Advance
  *  
  *    
  *
  * Parameters
  *     
  * 
  * Return Value
  *     
  *
  ***********************************************************************/
void Progress_Advance(HPROGRESS hProgress, UINT uPercentage, char *pszStatus)
{
	PPROGRESS_DATA pProgressData = (PPROGRESS_DATA)hProgress;

	if(pszStatus)
	{
		SendMessage(pProgressData->hStatusBar, SB_SETTEXT, 0, (LPARAM)pszStatus);
	}

	if(uPercentage)
	{
		SendMessage(pProgressData->hProgressBar, PBM_DELTAPOS, uPercentage, 0);
	}

}


 /***********************************************************************
  * Progress_CompleteAndClose
  *  
  *    
  *
  * Parameters
  *     
  * 
  * Return Value
  *     
  *
  ***********************************************************************/
void Progress_CompleteAndClose(HPROGRESS hProgress)
{
	PPROGRESS_DATA pProgressData = (PPROGRESS_DATA)hProgress;
    
	if(pProgressData->ClosePending == FALSE)
	{
		pProgressData->ClosePending = TRUE;
		EndDialog(pProgressData->hProgressWnd, 0);
	}

	if(pProgressData->HandleIsQuickProgress == FALSE)
	{
		LocalFree(pProgressData);
	}
}


 /***********************************************************************
  * Progress_CompleteAndCloseQuickWithReturn
  *  
  *    
  *
  * Parameters
  *     
  * 
  * Return Value
  *     
  *
  ***********************************************************************/
void Progress_CompleteAndCloseQuickWithReturn(HPROGRESS hProgress, PVOID pReturnValue)
{
	PPROGRESS_DATA pProgressData = (PPROGRESS_DATA)hProgress;
	
	if(pProgressData->HandleIsQuickProgress)
	{
		pProgressData->QuickProgressData.pReturnData = pReturnValue;
	}

	Progress_CompleteAndClose(hProgress);
}



 /***********************************************************************
  * Progress_QuickWorkThread
  *  
  *    
  *
  * Parameters
  *     
  * 
  * Return Value
  *     
  *
  ***********************************************************************/
DWORD WINAPI Progress_QuickWorkThread(PVOID pContext)
{
	PPROGRESS_DATA pProgressData = (PPROGRESS_DATA)pContext;
    pProgressData->QuickProgressData.pfnProgressBarWork((HPROGRESS)pProgressData, pProgressData->pContext);
	Progress_CompleteAndClose((HPROGRESS)pProgressData);
	return 0;
}



 /***********************************************************************
  * Progress_WindowProc
  *  
  *    
  *
  * Parameters
  *     
  * 
  * Return Value
  *     
  *
  ***********************************************************************/
BOOL WINAPI Progress_WindowProc(HWND hWnd, UINT uiMessage, WPARAM wParam, LPARAM lParam)
{
	BOOL ReturnValue = FALSE;
    
    switch(uiMessage)
	{
		case WM_INITDIALOG:
			 ReturnValue = TRUE;
			 break;
		default:
			 break;
	}

    return ReturnValue;
}