#include <windows.h>
#include <stdio.h>
#include <process.h>
#include <commctrl.h>

#include "dtstatus.rh"
#include "../dthres.h"

#ifdef _WIN64
    #pragma comment(lib,"../dthres64.lib")
#else
    #pragma comment(lib,"../dthres32.lib")
#endif

static HANDLE hSyncStatusChangeTerminate    = NULL;     // handle to terminate event used to close the status update thread
static HANDLE hSyncStatusChangeThread       = NULL;     // handle to the status update thread itself

//
// display the "traffic lights" to indicate status
//

void __fastcall ShowTrafficLights(HWND hDlg,DWORD dwStatus)
{
    ShowWindow(GetDlgItem(hDlg,IDC_UNKNOWN),    (dwStatus == SYNC_STATUS_UNKNOWN));
    ShowWindow(GetDlgItem(hDlg,IDC_GREEN),      (dwStatus == SYNC_STATUS_SYNCED));
    ShowWindow(GetDlgItem(hDlg,IDC_YELLOW),     (dwStatus == SYNC_STATUS_CHECKING));
    ShowWindow(GetDlgItem(hDlg,IDC_RED),        (dwStatus == SYNC_STATUS_UNSYNCED));
}

//
// update the running clock display
//

void __fastcall UpdateTime(HWND hDlg)
{
    static char     szLastDTTime[32]    = {""};
    static char     szLastRes   [16]    = {""};
    SYSTEMTIME      dt;
    LPSTR           pRes;
    char            szDTTime    [32];
    BOOL            bHighPerf;

    bHighPerf = GetLocalDomainTime(&dt);
    pRes        = bHighPerf ? "HiRes" : "Normal";

    if (strcmp(pRes,szLastRes) != 0)
    {
        strcpy_s(szLastRes,sizeof(szLastRes),pRes);
        SetDlgItemText(hDlg,IDC_RES,pRes);
    }

    sprintf_s(szDTTime,sizeof(szDTTime),"%02d:%02d:%02d.%003d",dt.wHour,dt.wMinute,dt.wSecond,dt.wMilliseconds);

    if (strcmp(szLastDTTime,szDTTime) != 0)
    {
        strcpy_s(szLastDTTime,sizeof(szLastDTTime),szDTTime);
        SetDlgItemText(hDlg,IDC_DT,szDTTime);
    }
}

//
// update the system status display
//

void __fastcall UpdateStatus(HWND hDlg)
{
    static DWORD    dwLastStatus        = -1;
    static LPSTR    pLastStatus         = NULL;
    static char     szLastCaption[128]  = {""};
    DWORD           dwRole;
    DWORD           dwVerMajor;
    DWORD           dwVerMinor;
    DWORD           dwVerDate;
    DWORD           dwThisStatus;
    LPSTR           pThisStatus;
    BOOL            bSuccess;
    char            szCaption   [128];

    bSuccess = IsDomainTimeRunning();
    if (bSuccess)
    {
        bSuccess = GetDomainTimeStatus(&dwThisStatus);
        if (bSuccess && dwThisStatus == dwLastStatus)
            return;

        strcpy_s(szCaption,sizeof(szCaption),"Domain Time II ");
        bSuccess = GetDomainTimeRole(&dwRole);
        switch (dwRole)
        {
        case ROLE_MASTER:       strcat_s(szCaption,sizeof(szCaption),"Master Server");         break;
        case ROLE_SLAVE:        strcat_s(szCaption,sizeof(szCaption),"Slave Server");          break;
        case ROLE_INDIE:        strcat_s(szCaption,sizeof(szCaption),"Independent Server");    break;
        case ROLE_CLIENT:       strcat_s(szCaption,sizeof(szCaption),"Full Client");           break;
        default:                strcat_s(szCaption,sizeof(szCaption),"Unknown Role");          break;
        }
        
        bSuccess = GetDomainTimeVersion(&dwVerMajor,&dwVerMinor,&dwVerDate);
        sprintf_s(szCaption+strlen(szCaption),sizeof(szCaption) - strlen(szCaption),"\r\nVersion %d.%d.b.%04d%04d",
            dwVerMajor,
            dwVerMinor,
            HIWORD(dwVerDate),
            LOWORD(dwVerDate));

        switch (dwThisStatus)
        {
        case SYNC_STATUS_CHECKING:
            pThisStatus     = "Checking time...";
            dwThisStatus    = SYNC_STATUS_CHECKING;
            break;
        
        case SYNC_STATUS_SYNCED:
            pThisStatus     = "System clock verified";
            dwThisStatus    = SYNC_STATUS_SYNCED;
            break;
        
        case SYNC_STATUS_UNSYNCED:
            pThisStatus     = "System clock not verified";
            dwThisStatus    = SYNC_STATUS_UNSYNCED;
            break;
        
        default:
            pThisStatus     = "Service not running";
            dwThisStatus    = SYNC_STATUS_UNKNOWN;
            break;
        }
    }
    else
    {
        strcpy_s(szCaption,sizeof(szCaption),"Domain Time Status");
        pThisStatus     = "Service not running";
        dwThisStatus    = SYNC_STATUS_UNKNOWN;
    }

    
    if (strcmp(szCaption,szLastCaption) != 0)
    {
        strcpy_s(szLastCaption,sizeof(szLastCaption),szCaption);
        SetDlgItemText(hDlg,IDC_CAPTION,szCaption);
    }

    if (pThisStatus != pLastStatus)
    {
        pLastStatus = pThisStatus;
        SetDlgItemText(hDlg,IDC_STATUS,pThisStatus);
    }

    dwLastStatus = dwThisStatus;
    ShowTrafficLights(hDlg,dwThisStatus);
}

//
// thread to monitor DomainTimeNotifyHandle for changes in status
//
// we could poll instead, but this demonstrates how to wait efficiently (most of
// the time, the status isn't changing)
//

DWORD WINAPI StatusChangeThread(LPVOID pParm)
{
    HANDLE  hEvents [2];
    DWORD   dwEvent;
    HWND    hDlg    = (HWND)pParm;

    hEvents[0]  = hSyncStatusChangeTerminate;
    hEvents[1]  = NULL;

    do
    {
        //
        // we might not be able to open the hSyncStatusEvent handle immediately -- this can
        // happen on Vista and later operating systems if the demo program starts up before
        // the Domain Time Service -- so if the handle is still NULL, wait a bit and try
        // opening it again
        //

        if (hEvents[1] == NULL) hEvents[1] = GetDomainTimeNotifyHandle();

        if (hEvents[1]) dwEvent = WaitForMultipleObjects(2,hEvents,FALSE,INFINITE);
        else            dwEvent = WaitForSingleObject(hEvents[0],10000);    // try again in 10 seconds

        if (dwEvent == WAIT_OBJECT_0 + 1) UpdateStatus(hDlg);               // status-change signalled, so update display
    } while (dwEvent != WAIT_OBJECT_0);

    if (hEvents[1]) CloseHandle(hEvents[1]);

    return 0;
}

//
// dialog box procedure
//

UINT_PTR CALLBACK MainProc (HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uiMsg)
    {
    case WM_INITDIALOG:
        UpdateTime  (hDlg);
        UpdateStatus(hDlg);
        hSyncStatusChangeTerminate  = CreateEvent(NULL,TRUE,FALSE,NULL);
        hSyncStatusChangeThread     = (HANDLE)_beginthreadex(NULL,0,StatusChangeThread,(void*)hDlg,0,NULL);
        SetTimer    (hDlg,1,1,NULL);
        return TRUE;

    case WM_TIMER:
        UpdateTime(hDlg);
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDCANCEL:
            KillTimer           (hDlg,1);
            SetEvent            (hSyncStatusChangeTerminate);
            WaitForSingleObject (hSyncStatusChangeThread,INFINITE);
            CloseHandle         (hSyncStatusChangeTerminate);
            CloseHandle         (hSyncStatusChangeThread);
            EndDialog           (hDlg,TRUE);
            break;
        }

        return TRUE;
    }

    return FALSE;
}

//
// main startup
//

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hUnused,LPSTR lpCmdLine,int nCmdShow)
{
    InitCommonControls();
    DialogBox(hInstance,MAKEINTRESOURCE(IDD_MAIN),NULL,MainProc);
    return 0;
}
