//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1998 - 1999
//
//  File:       progress.cpp
//
//--------------------------------------------------------------------------

// progress.cpp : implementation file
//

#include <pch.cpp>

#pragma hdrstop

#include "clibres.h"
#include "progress.h"

// defines

#ifdef _DEBUG
//#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

enum MYDIALOGBEHAVIORENUM
{
    enumPERCENTCOMPLETEBEHAVIOR = 0,
    enumPROGRESSBARWITHTIMEOUT,
};

typedef struct _PROGRESSPROC_LPARAM
{
    HINSTANCE           hInstance;
    HWND                hwndParent;
    UINT                iRscJobDescription;
    MYDIALOGBEHAVIORENUM enumWhichBehavior;
    DWORD               dwTickerUpperRange;
    DBBACKUPPROGRESS*   pdbp;
} PROGRESSPROC_LPARAM, *PPROGRESSPROC_LPARAM;


static BOOL s_fDisableProgressDialogs = 0;
static BOOL s_fIKnow = 0;

BOOL FICanShowDialogs()
{
   if (s_fIKnow != TRUE)
   {
      s_fIKnow = TRUE;
      DWORD dwVal;

      if (S_OK == myGetCertRegDWValue(
         NULL,
         NULL,
         NULL,
         L"DisableProgress",
         &dwVal))
      {
         s_fDisableProgressDialogs = (dwVal != 0);
      }
   }
   return ! s_fDisableProgressDialogs;
}



////////////////////////////////////////////////////////////////////////////////
// show a progress dialog

int     g_iTimeoutTicks = 0;
BOOL    g_fUseTimer;

INT_PTR CALLBACK dlgProcProgress(
  HWND hwndDlg,  
  UINT uMsg,     
  WPARAM wParam, 
  LPARAM lParam  )
{
    PPROGRESSPROC_LPARAM pLParam = NULL;

    switch(uMsg)
    {
    case WM_INITDIALOG:
        {
            HWND hwndProgressBar;
            
            pLParam = (PPROGRESSPROC_LPARAM)lParam;
            SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (ULONG_PTR)pLParam);

            hwndProgressBar = GetDlgItem(hwndDlg, IDD_PROGRESS_BAR);
			
            {
                RECT rcParent, rcDlg, rcScreenArea;
                GetWindowRect(pLParam->hwndParent, &rcParent);
            	GetWindowRect(hwndDlg, &rcDlg);
                SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcScreenArea, NULL);

                // calc centers
                int xLeft = (rcParent.left + rcParent.right) / 2 - (rcDlg.right - rcDlg.left) / 2;
                int yTop = (rcParent.top + rcParent.bottom) / 2 - (rcDlg.bottom - rcDlg.top) / 2;

                // careful: if the dialog is outside the screen, move it inside
                if (xLeft < rcScreenArea.left)
	                xLeft = rcScreenArea.left;
                else if (xLeft + (rcDlg.right - rcDlg.left) > rcScreenArea.right)
	                xLeft = rcScreenArea.right - (rcDlg.right - rcDlg.left);

                if (yTop < rcScreenArea.top)
	                yTop = rcScreenArea.top;
                else if (yTop + (rcDlg.bottom - rcDlg.top) > rcScreenArea.bottom)
	                yTop = rcScreenArea.bottom - (rcDlg.bottom - rcDlg.top);

                // map screen coordinates to child coordinates
                SetWindowPos(hwndDlg, HWND_TOPMOST, xLeft, yTop, -1, -1,
                    SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
            }
    
            {
                DWORD dwStyle;
                dwStyle = GetWindowLong(hwndProgressBar, GWL_STYLE);
                SetWindowLong(hwndProgressBar, GWL_STYLE, (dwStyle | PBS_SMOOTH)); 
            }

            // Set the range and increment of the progress bar. 
            if (pLParam->enumWhichBehavior == enumPROGRESSBARWITHTIMEOUT)
            {
                SendMessage(hwndProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0, pLParam->dwTickerUpperRange));
                SendMessage(hwndProgressBar, PBM_SETSTEP, (WPARAM) 1, 0); 
            }
            else
            {
                SendMessage(hwndProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0, 300));
            }
            SendMessage(hwndProgressBar, PBM_SETPOS, (WPARAM)0, 0);
            
            // set job description if specified
            if (pLParam->iRscJobDescription != 0)
            {
                WCHAR szJobDesc[MAX_PATH];
                if (0 != LoadString(
                             pLParam->hInstance,
                             pLParam->iRscJobDescription,
                             szJobDesc,
                             MAX_PATH))
                    SetDlgItemText(hwndDlg, IDC_JOB_DESCRIPTION, szJobDesc);
            }

            return 1;
        }
    case WM_DESTROY:
        {
            pLParam = (PPROGRESSPROC_LPARAM)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
            if (NULL == pLParam)
                break;

            SetForegroundWindow(pLParam->hwndParent);

            LocalFree(pLParam);
            pLParam = NULL;
            SetWindowLongPtr(hwndDlg, GWLP_USERDATA, NULL);
        }
    case PBM_STEPIT:
        {
            pLParam = (PPROGRESSPROC_LPARAM)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
            if (NULL == pLParam)
                break;

            HWND hwndProgressBar = GetDlgItem(hwndDlg, IDD_PROGRESS_BAR);

            if (pLParam->enumWhichBehavior == enumPROGRESSBARWITHTIMEOUT)
                SendMessage(hwndProgressBar, PBM_STEPIT, 0, 0);
            else
            {
                DWORD wProgress = pLParam->pdbp->dwDBPercentComplete + 
                                pLParam->pdbp->dwLogPercentComplete + 
                                pLParam->pdbp->dwTruncateLogPercentComplete ;
                
                DWORD wTop = (DWORD)SendMessage(hwndProgressBar,
		                                PBM_GETRANGE,
						FALSE,
						NULL);
                if (wProgress == wTop)
                {
		    SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, S_FALSE);     // we're done!
                    return TRUE;
                }
                else
                    SendMessage(hwndProgressBar, PBM_SETPOS, wProgress, 0); // keep incrementing
            }


            break;
        }
    case PBM_SETPOS:
        {
            HWND hwndProgressBar = GetDlgItem(hwndDlg, IDD_PROGRESS_BAR);
            LRESULT wTop = SendMessage(hwndProgressBar, PBM_GETRANGE, FALSE, NULL);
            
            // if we're not yet at the top make it so
            if (wTop != SendMessage(hwndProgressBar, PBM_GETPOS, 0, 0))
            {
                SendMessage(hwndProgressBar, PBM_SETPOS, (WPARAM)wTop, 0);
                Sleep(400);
            }
        }
    default:
        break;
    }
    return 0;
}

#define PROGRESS_TICKS_PER_SEC 3

DWORD WINAPI StartTimerThread(  LPVOID lpParameter )
{
    if (FICanShowDialogs())
    { 
    PPROGRESSPROC_LPARAM  psParam = (PPROGRESSPROC_LPARAM)lpParameter;

    HWND hwndProgressDlg = CreateDialogParam(  
        psParam->hInstance,
        MAKEINTRESOURCE(IDD_PROGRESS_BOX),
        NULL,
        dlgProcProgress, 
        (LPARAM)lpParameter);
    if (NULL == hwndProgressDlg)
       return 0;

    ShowWindow(hwndProgressDlg, SW_SHOW);
    UpdateWindow(hwndProgressDlg);

    // if not timer based, go forever
    // if timer based, go while timersec is +
    while ((!g_fUseTimer) ||
           (g_iTimeoutTicks-- > 0))
    {
        SendMessage(hwndProgressDlg, PBM_STEPIT, 0, 0);
        UpdateWindow(hwndProgressDlg);
        Sleep(1000/PROGRESS_TICKS_PER_SEC);
    }

    // send "fill the indicator" 
    SendMessage(hwndProgressDlg, PBM_SETPOS, 0, 0);
    
    DestroyWindow(hwndProgressDlg);
    }
    return 0;
}

// callable APIs: Start/End ProgressDlg
BOOL FProgressDlgRunning()
{
    return (!g_fUseTimer || (g_iTimeoutTicks > 0));
}

HANDLE
StartProgressDlg(
    HINSTANCE hInstance,
    HWND      hwndParent,
    DWORD     dwTickerSeconds,
    DWORD     dwTimeoutSeconds,
    UINT      iRscJobDescription)
{
    HANDLE hProgressThread = NULL;
    DWORD dwThread;
    PPROGRESSPROC_LPARAM psParam = NULL;
    
    INITCOMMONCONTROLSEX sCommCtrl;
    sCommCtrl.dwSize = sizeof(sCommCtrl);
    sCommCtrl.dwICC = ICC_PROGRESS_CLASS;
    if (!InitCommonControlsEx(&sCommCtrl))
        goto Ret;

    g_fUseTimer = dwTimeoutSeconds != 0;
    g_iTimeoutTicks = (dwTimeoutSeconds * PROGRESS_TICKS_PER_SEC);
    
    // dialog frees this
    psParam = (PPROGRESSPROC_LPARAM)LocalAlloc(LMEM_FIXED, sizeof(PROGRESSPROC_LPARAM));
    if (psParam == NULL)
        goto Ret;

    psParam->hInstance = hInstance;
    psParam->hwndParent = hwndParent;
    psParam->enumWhichBehavior = enumPROGRESSBARWITHTIMEOUT;
    psParam->dwTickerUpperRange = dwTickerSeconds * PROGRESS_TICKS_PER_SEC;
    psParam->iRscJobDescription = iRscJobDescription;
    psParam->pdbp = NULL;


    hProgressThread = 
        CreateThread(
            NULL,
            0,
            StartTimerThread,
            (void*)psParam,
            0,
            &dwThread);
Ret:
    if (NULL == hProgressThread)
        LocalFree(psParam);

    return hProgressThread;
}

void EndProgressDlg(HANDLE hProgressThread)
{
    // end countdown immediately
    g_iTimeoutTicks = 0;
    if (!g_fUseTimer)
    {
        // make the controlling thread suddenly aware of the timer
        g_fUseTimer = TRUE;
    }

    // don't return until we're certain the progress dlg is gone
    while(TRUE)
    {
        DWORD dwExitCode;
        // break on error
        if (!GetExitCodeThread(hProgressThread, &dwExitCode) )
            break;

        // continue until goes away
        if (STILL_ACTIVE != dwExitCode)
            break;

        Sleep(100);
    }

    CloseHandle(hProgressThread);
}

///////////////////////////////////////////////////////
// %age complete progress indicator

DWORD WINAPI StartPercentCompleteThread(  LPVOID lpParameter )
{
    if (FICanShowDialogs())
    {
    PPROGRESSPROC_LPARAM  psParam = (PPROGRESSPROC_LPARAM)lpParameter;

    HWND hwndProgressDlg = CreateDialogParam(  
        psParam->hInstance,
        MAKEINTRESOURCE(IDD_PROGRESS_BOX),
        NULL,
        dlgProcProgress, 
        (LPARAM)lpParameter);

if (NULL == hwndProgressDlg) {GetLastError(); return 0;}

    ShowWindow(hwndProgressDlg, SW_SHOW);
    Sleep(0);

    while (TRUE)
    {
        if (ERROR_SUCCESS != SendMessage(hwndProgressDlg, PBM_STEPIT, 0, 0))
            break;

        UpdateWindow(hwndProgressDlg);
        Sleep(0);
        Sleep(1000/PROGRESS_TICKS_PER_SEC);
    }

    // send "fill the indicator" 
    SendMessage(hwndProgressDlg, PBM_SETPOS, 0, 0);
    
    DestroyWindow(hwndProgressDlg);
    }

    return 0;
}

HANDLE
StartPercentCompleteDlg(
    HINSTANCE  hInstance,
    HWND       hwndParent,
    UINT       iRscJobDescription,
    DBBACKUPPROGRESS *pdbp)
{
    HANDLE hProgressThread = NULL;
    DWORD dwThread;
    PPROGRESSPROC_LPARAM psParam = NULL;

    g_fUseTimer = FALSE;
    g_iTimeoutTicks = 0;    // no timeout

    INITCOMMONCONTROLSEX sCommCtrl;
    sCommCtrl.dwSize = sizeof(sCommCtrl);
    sCommCtrl.dwICC = ICC_PROGRESS_CLASS;
    if (!InitCommonControlsEx(&sCommCtrl))
        goto Ret;

    // dialog frees this
    psParam = (PPROGRESSPROC_LPARAM)LocalAlloc(LMEM_FIXED, sizeof(PROGRESSPROC_LPARAM));
    if (psParam == NULL)
        goto Ret;

    psParam->hInstance = hInstance;
    psParam->hwndParent = hwndParent;
    psParam->enumWhichBehavior = enumPERCENTCOMPLETEBEHAVIOR;
    psParam->dwTickerUpperRange = 300;
    psParam->iRscJobDescription = iRscJobDescription;
    psParam->pdbp = pdbp;
    
    hProgressThread = 
        CreateThread(
            NULL,
            0,
            StartPercentCompleteThread,
            (void*)psParam,
            0,
            &dwThread);
Ret:
    if (NULL == hProgressThread)
        LocalFree(psParam);

    return hProgressThread;
}

void EndPercentCompleteDlg(HANDLE hProgressThread)
{
    // don't return until we're certain the progress dlg is gone
    while(TRUE)
    {
        DWORD dwExitCode;
        // break on error
        if (!GetExitCodeThread(hProgressThread, &dwExitCode) )
            break;

        // continue until goes away
        if (STILL_ACTIVE != dwExitCode)
            break;

        Sleep(100);
    }

    CloseHandle(hProgressThread);
}