/*----------------------------------------------------------------------------*\
|                                                                              |
|   ta.c - Timer Device Driver Test Application                                |
|                                                                              |
|                                                                              |
|   History:                                                                   |
|                                                                              |
|       Created     Glenn Steffler (w-GlennS) 24-Jan-1990                      |
|                                                                              |
\*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*\
|                                                                              |
|   i n c l u d e   f i l e s                                                  |
|                                                                              |
\*----------------------------------------------------------------------------*/

#include <windows.h>
#include <mmsystem.h>
#include <port1632.h>
#include <stdio.h>
#include <string.h>
#include "ta.h"

/*----------------------------------------------------------------------------*\
|                                                                              |
|   g l o b a l   v a r i a b l e s                                            |
|                                                                              |
\*----------------------------------------------------------------------------*/
extern HWND hdlgDelay;

static  char    szAppName[]="Timer Device Test Application";

HANDLE  hInstApp;
HWND    hwndApp;

WORD    wHandlerError = 0;
BOOL    bHandlerHit = FALSE;      // was timer event handler hit since last
int     dyFont;
int     dxFont;
HFONT   hfontApp;
HBRUSH  hbrWindow;
extern  int         nEvents;
extern  EVENTLIST   EventList[];
extern  HWND        hdlgModeless;

LPTIMECALLBACK lpTimeCallback;    // timer callback function

FARPROC lpfnDelayDlg = NULL;      // modeless Delay dialog function pointer
BOOL fSystemTime = TRUE;
BOOL fRealTime = TRUE;
BOOL fFirstTime = TRUE;

/*----------------------------------------------------------------------------*\
|                                                                              |
|   f u n c t i o n   d e f i n i t i o n s                                    |
|                                                                              |
\*----------------------------------------------------------------------------*/

LONG FAR PASCAL AppWndProc (HWND hwnd, unsigned uiMessage, UINT wParam, LONG lParam);
BOOL fDialog(int id,HWND hwnd,FARPROC lpfn);

LONG  NEAR PASCAL AppCommand(HWND hwnd, unsigned msg, UINT wParam, LONG lParam);

extern BOOL FAR PASCAL fnAboutDlg(
        HWND hDlg, unsigned uiMessage, UINT wParam, LONG lParam );
extern BOOL FAR PASCAL fnDelayDlg(
        HWND hDlg, unsigned uiMessage, UINT wParam, LONG lParam );
extern BOOL FAR PASCAL fnFileDlg(
        HWND hDlg, unsigned uiMessage, UINT wParam, LONG lParam );

/*----------------------------------------------------------------------------*\
|   AppInit( hInst, hPrev)                                                     |
|                                                                              |
|   Description:                                                               |
|       This is called when the application is first loaded into               |
|       memory.  It performs all initialization that doesn't need to be done   |
|       once per instance.                                                     |
|                                                                              |
|   Arguments:                                                                 |
|       hInstance       instance handle of current instance                    |
|       hPrev           instance handle of previous instance                   |
|                                                                              |
|   Returns:                                                                   |
|       TRUE if successful, FALSE if not                                       |
|                                                                              |
\*----------------------------------------------------------------------------*/
BOOL AppInit(HANDLE hInst,HANDLE hPrev,UINT sw,LPSTR szCmdLine)
{
    WNDCLASS cls;
    int      dx,dy;
    char     ach[80];
    HMENU    hmenu;
    TEXTMETRIC tm;
    HDC        hdc;

    /* Save instance handle for DialogBoxs */
    hInstApp = hInst;

    if (!hPrev) {
        /*
         *  Register a class for the main application window
         */
        cls.hCursor        = LoadCursor(NULL,IDC_ARROW);
        cls.hIcon          = LoadIcon(hInst,MAKEINTATOM(ID_APP));
        cls.lpszMenuName   = MAKEINTATOM(ID_APP);
        cls.lpszClassName  = MAKEINTATOM(ID_APP);
        cls.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
        cls.hInstance      = hInst;
        cls.style          = CS_BYTEALIGNCLIENT | CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
        cls.lpfnWndProc    = AppWndProc;
        cls.cbWndExtra     = 0;
        cls.cbClsExtra     = 0;

        if (!RegisterClass(&cls))
            return FALSE;
    }

    dx = GetSystemMetrics (SM_CXSCREEN);
    dy = GetSystemMetrics (SM_CYSCREEN);

////hfontApp = GetStockObject(ANSI_VAR_FONT);
    hfontApp = GetStockObject(ANSI_FIXED_FONT);

    hdc = GetDC(NULL);
    SelectObject(hdc, hfontApp);
    GetTextMetrics(hdc, &tm);
    dxFont = tm.tmAveCharWidth;
    dyFont = tm.tmHeight;
    ReleaseDC(NULL,hdc);

    hbrWindow = CreateSolidBrush(GetSysColor(COLOR_WINDOW));

    hwndApp = CreateWindow (MAKEINTATOM(ID_APP),    // Class name
                            szAppName,              // Caption
                            WS_OVERLAPPEDWINDOW,    // Style bits
                            CW_USEDEFAULT, 0,       // Position
                            ((2*dx)/3),dy/2,        // Size
                            (HWND)NULL,             // Parent window (no parent)
                            (HMENU)NULL,            // use class menu
                            (HANDLE)hInst,          // handle to window instance
                            (LPSTR)NULL             // no params to pass on
                           );
    ShowWindow(hwndApp,sw);

    lpTimeCallback = (LPTIMECALLBACK)MakeProcInstance(TimeCallback,hInst);

    return TRUE;
}

/*----------------------------------------------------------------------------*\
|   WinMain( hInst, hPrev, lpszCmdLine, cmdShow )                              |
|                                                                              |
|   Description:                                                               |
|       The main procedure for the App.  After initializing, it just goes      |
|       into a message-processing loop qntil it gets a WM_QUIT message         |
|       (meaning the app was closed).                                          |
|                                                                              |
|   Arguments:                                                                 |
|       hInst           instance handle of this instance of the app            |
|       hPrev           instance handle of previous instance, NULL if first    |
|       szCmdLine       ->null-terminated command line                         |
|       cmdShow         specifies how the window is initially displayed        |
|                                                                              |
|   Returns:                                                                   |
|       The exit code as specified in the WM_QUIT message.                     |
|                                                                              |
\*----------------------------------------------------------------------------*/
MMain(hInst, hPrev, szCmdLine, sw)
{
    MSG     msg;

    /* Call initialization procedure */

    if (!AppInit(hInst,hPrev,sw,szCmdLine))
        return FALSE;

    /* check for messages from Windows and process them */
    /* if no messages, perform some idle function */

    while(1) {
        if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) {
            if( hdlgModeless == 0 || !IsDialogMessage( hdlgModeless, &msg ) ) {
                /* got a message to process */
                if( msg.message == WM_QUIT ) break;
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
        else {
            Idle();
        }
    }

    DeleteObject(hbrWindow);
    return (msg.wParam);
}
}


static  LONG lError2=0l;
static  LONG lDelta=0l;
static  LONG lTDelta=0l;

static  LONG lErrorMax=0l;
static  LONG lErrorMin=0l;

/*----------------------------------------------------------------------------*\
|   AppPaint(hwnd)                                                             |
|                                                                              |
|   Description:                                                               |
|       The paint function.  Right now this does nothing.                      |
|                                                                              |
|   Arguments:                                                                 |
|       hwnd             window painting into                                  |
|                                                                              |
|   Returns:                                                                   |
|       nothing                                                                |
|                                                                              |
\*----------------------------------------------------------------------------*/
void PASCAL AppPaint(HWND hwnd, HDC hdc)
{
    typedef struct tag_booga {
        int     *pnValue;
        char    *szDescription;
    } BOOGA;

    static  BOOGA   bgTell[] ={ { &wHandlerError,   "Handler Error" },
                                { &nEvents,         "Number Of Events" }};
    RECT    rc;
    char    szText[100];
    int     i;
    MMTIME  mmt;
    DWORD   ms;
    DWORD   dw,dwRTC;
    LONG    lError,lTError;
    BOOL    fGetDC;
    int     len;
    int hrs, min, sec;
    SYSTEMTIME Time;

    if (fGetDC = (hdc == NULL))
        hdc = GetDC(hwnd);

    SelectObject(hdc, hfontApp);

    GetClientRect(hwnd,&rc);

    SetTextColor(hdc,GetSysColor(COLOR_WINDOWTEXT));
    SetBkColor(hdc,GetSysColor(COLOR_WINDOW));

    rc.top    = dxFont;
    rc.left   = dxFont;
    rc.bottom = rc.top+dyFont;

    GetSystemTime(&Time);
    if (fSystemTime)
        {
        timeGetSystemTime(&mmt, sizeof(mmt));


        dwRTC = (Time.wSecond * 1000l) +
                (Time.wMinute * 1000l * 60l) +
                (Time.wHour * 1000l * 60l * 60l);

        if (fRealTime)
            {
            len = wsprintf(szText, "RealTime = [%08ld ms] %02d hrs %02d min %02d.000 sec",
                    dwRTC,
                    Time.wHour,
                    Time.wMinute,
                    Time.wSecond);

            ExtTextOut( hdc, rc.left, rc.top, ETO_OPAQUE, &rc, szText, len, NULL);
            rc.top     = rc.bottom;
            rc.bottom += dyFont;

            dw = dwRTC;
            }

        mmt.u.ms += lDelta;

        lError = (long)dwRTC - (long)mmt.u.ms;

        if (lDelta == 0l)
            {
            lDelta = lError;
            mmt.u.ms += lDelta;
            }

        hrs = (WORD)(mmt.u.ms / (1000l*60l*60l));
        min = (WORD)(mmt.u.ms / (1000*60l)) % 60;
        sec = (WORD)(mmt.u.ms / 1000l) % 60;

        len = wsprintf( szText, "SysTime  = [%08ld ms] %02d hrs %02d min %02d.%03d sec",
                  mmt.u.ms,
                  hrs,min,sec,
                  (int)(mmt.u.ms % 1000L) );

        ExtTextOut( hdc, rc.left, rc.top, ETO_OPAQUE, &rc, szText, len, NULL);
        rc.top     = rc.bottom;
        rc.bottom += dyFont;


        if (fFirstTime)
            {
            fFirstTime = FALSE;
            }
        else
            {
            lErrorMin = min(lErrorMin,lError);
            lErrorMax = max(lErrorMax,lError);
            }

        len = wsprintf( szText, "SysTime Error:%8ld ms Max:%8ld Min:%8ld delta: %8ld",
            -lError,
            -lErrorMin,
            -lErrorMax,
            lError-lError2);

        lError2 = lError;

        ExtTextOut( hdc, rc.left, rc.top, ETO_OPAQUE, &rc, szText, len, NULL);
        rc.top     = rc.bottom;
        rc.bottom += dyFont;
        }

    for( i=0; i < sizeof(bgTell)/sizeof(BOOGA); i++ ) {
        if( *(bgTell[i].pnValue) ) {
            len = wsprintf( szText, "%ls  = [%d] ",
                 (LPSTR)bgTell[i].szDescription, *(bgTell[i].pnValue) );

            ExtTextOut( hdc, rc.left, rc.top, ETO_OPAQUE, &rc, szText, len, NULL);
            rc.top     = rc.bottom;
            rc.bottom += dyFont;
        }
    }
    for( i=0; i < MAXEVENTS; i++ ) {
        if( EventList[i].bActive ) {
            len = wsprintf( szText, "Event %2.2x Count=[%ld]%c Errors=%ld (%d%%) dtime=%ld Min=%ld Max=%ld",
                (int)EventList[i].nID,
                EventList[i].dwCount,
                (char)(EventList[i].bHit ? '*' : '-'),
                EventList[i].dwError,
                EventList[i].dwCount ? (int)(EventList[i].dwError * 100 / EventList[i].dwCount) : 0,
                EventList[i].dtime,
                EventList[i].dtimeMin,
                EventList[i].dtimeMax );

            EventList[i].bHit = FALSE;

            ExtTextOut( hdc, rc.left, rc.top, ETO_OPAQUE, &rc, szText, len, NULL);
            rc.top     = rc.bottom;
            rc.bottom += dyFont;
        }
    }

    i = rc.top;
    GetClientRect(hwnd,&rc);
    rc.top = i;
    FillRect(hdc, &rc, hbrWindow);
////ExtTextOut( hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);

    if (fGetDC)
        ReleaseDC(hwnd,hdc);

    return;
    #undef  BOOGA
}

/*----------------------------------------------------------------------------*\
|   Idle( void )                                                               |
|                                                                              |
|   Description:                                                               |
|       Idle loop function...displays time values while windows idles          |
|                                                                              |
|   Arguments:                                                                 |
|       none                                                                   |
|                                                                              |
|   Returns:                                                                   |
|       nothing                                                                |
|                                                                              |
\*----------------------------------------------------------------------------*/
void PASCAL Idle( )
{
    if(hwndApp && bHandlerHit || fSystemTime) {
        AppPaint( hwndApp, NULL );
        bHandlerHit = FALSE;
    }
    else
        WaitMessage();

    return;
}


/*----------------------------------------------------------------------------*\
|                                                                              |
|   w i n d o w   p r o c s                                                    |
|                                                                              |
\*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*\
|   AppWndProc( hwnd, uiMessage, wParam, lParam )                              |
|                                                                              |
|   Description:                                                               |
|       The window proc for the app's main (tiled) window.  This processes all |
|       of the parent window's messages.                                       |
|                                                                              |
|   Arguments:                                                                 |
|       hwnd            window handle for the window                           |
|       uiMessage       message number                                         |
|       wParam          message-dependent                                      |
|       lParam          message-dependent                                      |
|                                                                              |
|   Returns:                                                                   |
|       0 if processed, nonzero if ignored                                     |
|                                                                              |
\*----------------------------------------------------------------------------*/
LONG FAR PASCAL AppWndProc(hwnd, msg, wParam, lParam)
    HWND     hwnd;
    unsigned msg;
    UINT     wParam;
    long     lParam;
{
    BOOL        f;
    HDC         hdc;
    PAINTSTRUCT ps;

    switch (msg) {
        case WM_CREATE:
            break;

        case WM_COMMAND:
            return AppCommand(hwnd,msg,wParam,lParam);

        case WM_CLOSE:
            hwndApp = NULL;
            if (hdlgDelay) {
                PostMessage (hdlgDelay,WM_CLOSE,0,0l);
            }
            break;

        case WM_DESTROY:
            PostQuitMessage (0);

            // FALL THRU AND DESTROY ALL REMAINING OBJECTS / TIMER EVENTS

        case WM_ENDSESSION:
            KillAllEvents();
            if( lpTimeCallback ) {
                FreeProcInstance(lpTimeCallback);
                lpTimeCallback = NULL;
            }
            if( lpfnDelayDlg ) {
                FreeProcInstance(lpfnDelayDlg);
            }
            break;

        case WM_ERASEBKGND:
            break;

        case WM_PAINT:
            BeginPaint(hwnd, &ps);
            AppPaint( hwnd, ps.hdc );
            EndPaint(hwnd, &ps);
            return 0L;

        case MM_TIMEEVENT:
            if( hdlgDelay != NULL )
                SendMessage(hdlgDelay,msg,wParam,lParam);
            break;

    }
    return DefWindowProc(hwnd,msg,wParam,lParam);
}

LONG NEAR PASCAL AppCommand (hwnd, msg, wParam, lParam)
    HWND     hwnd;
    unsigned msg;
    UINT     wParam;
    long     lParam;
{
    HANDLE  hInst;

    switch(LOWORD(wParam))
    {
        case MENU_ABOUT:
            fDialog(ABOUTDLG,hwnd,fnAboutDlg);
            break;

        case MENU_DELAY:
            if( hdlgDelay ) {
                ShowWindow( hdlgDelay, SW_SHOWNORMAL );
                SetFocus( hdlgDelay );
            } else {
                hInst = GetWindowLong(hwnd,GWL_HINSTANCE);
                lpfnDelayDlg = MakeProcInstance(fnDelayDlg,hInst);
                hdlgModeless = CreateDialog(hInst,
                    MAKEINTRESOURCE(DELAYDLG),hwnd,lpfnDelayDlg);
            }
            break;

        case MENU_TIME:
            fSystemTime = !fSystemTime;
            if (!fSystemTime)
                {
                fFirstTime = TRUE;
                }
            lTDelta = 0l;
            lDelta = lErrorMin = lErrorMax = 0l;
            break;

        case MENU_LOAD:
        case MENU_SAVE:
            if( fDialog(FILEDLG,hwnd,fnFileDlg) )
                // call function to Save/Load session
                break;
            break;

        case MENU_EXIT:
            PostMessage(hwnd,WM_CLOSE,0,0L);
            break;

    }
    return 0L;
}

/*----------------------------------------------------------------------------*\
|   fDialog(id,hwnd,lpfn)                                                      |
|                                                                              |
|   Description:                                                               |
|       This function displays a dialog box and returns the exit code.         |
|       the function passed will have a proc instance made for it.             |
|                                                                              |
|   Arguments:                                                                 |
|       id              resource id of dialog to display                       |
|       hwnd            parent window of dialog                                |
|       lpfn            dialog message function                                |
|                                                                              |
|   Returns:                                                                   |
|       exit code of dialog (what was passed to EndDialog)                     |
|                                                                              |
\*----------------------------------------------------------------------------*/
BOOL fDialog(int id,HWND hwnd,FARPROC lpfn)
{
    BOOL        f;
    HANDLE      hInst;

    hInst = GetWindowLong(hwnd,GWL_HINSTANCE);
    lpfn  = MakeProcInstance(lpfn,hInst);
    f = DialogBox(hInst,MAKEINTRESOURCE(id),hwnd,lpfn);
    FreeProcInstance (lpfn);
    return f;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/
void FAR PASCAL PeriodicCallback( UINT wID, UINT msg, DWORD dwUser, DWORD dwTime, DWORD dw2)
{
    int i = LOWORD(dwUser);

    DWORD   time;

    bHandlerHit = TRUE;

    time = timeGetTime();

    EventList[i].dtime    = time - EventList[i].time;
    EventList[i].dtimeMin = min(EventList[i].dtime,EventList[i].dtimeMin);
    EventList[i].dtimeMax = max(EventList[i].dtime,EventList[i].dtimeMax);
    EventList[i].time     = time;
    EventList[i].dwCount++;
    EventList[i].bHit     = TRUE;

//  if (abs((int)EventList[i].dtime-(int)EventList[i].teEvent.wDelay) > (int)EventList[i].teEvent.wResolution)
//       EventList[i].dwError++;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/
void FAR PASCAL OneShotCallback( UINT wID, UINT msg, DWORD dwUser, DWORD dwTime, DWORD dw2)
{
    int i = LOWORD(dwUser);

    DWORD   time;

    bHandlerHit = TRUE;

    time = timeGetTime();

    EventList[i].dtime    = time - EventList[i].time;
    EventList[i].dtimeMin = min(EventList[i].dtime,EventList[i].dtimeMin);
    EventList[i].dtimeMax = max(EventList[i].dtime,EventList[i].dtimeMax);
    EventList[i].time     = time;
    EventList[i].dwCount++;
    EventList[i].bHit     = TRUE;

//  if (abs((int)EventList[i].dtime-(int)EventList[i].teEvent.wDelay) > (int)EventList[i].teEvent.wResolution)
//      EventList[i].dwError++;
}