/*++
 *
 *  WOW v1.0
 *
 *  Copyright (c) 1991, Microsoft Corporation
 *
 *  WUTMR.C
 *  WOW32 16-bit User Timer API support
 *
 *  History:
 *  Created 07-Mar-1991 by Jeff Parsons (jeffpar)
 *          24-Feb-1993 reworked to use array of timer functions - barryb
--*/


#include "precomp.h"
#pragma hdrstop

MODNAME(wutmr.c);

LIST_ENTRY TimerList;

// Element Zero is unused.

STATIC PTMR aptmrWOWTimers[] = {
                                 NULL, NULL, NULL, NULL,
                                 NULL, NULL, NULL, NULL,
                                 NULL, NULL, NULL, NULL,
                                 NULL, NULL, NULL, NULL,
                                 NULL, NULL, NULL, NULL,
                                 NULL, NULL, NULL, NULL,
                                 NULL, NULL, NULL, NULL
                               };


STATIC TIMERPROC afnTimerFuncs[] = {
                        NULL,       W32Timer1,  W32Timer2,  W32Timer3,
                        W32Timer4,  W32Timer5,  W32Timer6,  W32Timer7,
                        W32Timer8,  W32Timer9,  W32Timer10, W32Timer11,
                        W32Timer12, W32Timer13, W32Timer14, W32Timer15,
                        W32Timer16, W32Timer17, W32Timer18, W32Timer19,
                        W32Timer20, W32Timer21, W32Timer22, W32Timer23,
                        W32Timer24, W32Timer25, W32Timer26, W32Timer27
                        };


/* Timer mapping functions
 *
 * The basic 16-bit timer mapping operations are Add, Find and Free.  When
 * a 16-bit app calls SetTimer, we call Win32's SetTimer with W32TimerProc
 * in place of the 16-bit proc address.  Assuming the timer is successfully
 * allocated, we add the timer to our own table, recording the 16-bit proc
 * address.
 */


//
// Search for a timer by its 16-bit information.  Looks in the list of
// active timers.  If the timer is found by this routine, then SetTimer()
// has been called and KillTimer() has not yet been called.
//
PTMR IsDuplicateTimer16(HWND16 hwnd16, HTASK16 htask16, WORD wIDEvent)
{
    register PTMR ptmr;
    register INT iTimer;

    //
    // Excel calls SetTimer with hwnd==NULL but dispatches the
    // WM_TIMER messages with hwnd!=NULL.  so call it a match if
    // hwnd16!=NULL and ptmr->hwnd16==NULL
    //

    for (iTimer=1; iTimer<NUMEL(aptmrWOWTimers); iTimer++) {

        ptmr = aptmrWOWTimers[iTimer];

        if (ptmr) {
            if (LOWORD(ptmr->dwEventID) == wIDEvent &&
                ptmr->htask16 == htask16 &&
                (ptmr->hwnd16 == hwnd16 || !ptmr->hwnd16)) {

                return ptmr;
            }
        }
    }

    return NULL;
}

//
// Search for a timer by its 32-bit information.  Looks in the list of
// all timers (including those that have already been killed by KillTimer().
//
//
PTMR FindTimer32(HWND16 hwnd16, DWORD dwIDEvent)
{
    register PTMR ptmr;
    HAND16 htask16;

    htask16 = CURRENTPTD()->htask16;

    //
    // Excel calls SetTimer with hwnd==NULL but dispatches the
    // WM_TIMER messages with hwnd!=NULL.  so call it a match if
    // hwnd16!=NULL and ptmr->hwnd16==NULL
    //

    for (ptmr = (PTMR)TimerList.Flink; ptmr != (PTMR)&TimerList; ptmr = (PTMR)ptmr->TmrList.Flink) {

        if (ptmr->dwEventID == dwIDEvent &&
            ptmr->htask16 == htask16 &&
            (ptmr->hwnd16 == hwnd16 || (hwnd16 && !ptmr->hwnd16))) {

            return ptmr;
        }
    }

    return (PTMR)NULL;
}


//
// Search for a timer by its 16-bit information.  Looks in the list of
// all timers (including those that have already been killed by KillTimer().
//
//
PTMR FindTimer16(HWND16 hwnd16, HTASK16 htask16, WORD wIDEvent)
{
    register PTMR ptmr;

    //
    // Excel calls SetTimer with hwnd==NULL but dispatches the
    // WM_TIMER messages with hwnd!=NULL.  so call it a match if
    // hwnd16!=NULL and ptmr->hwnd16==NULL
    //

    for (ptmr = (PTMR)TimerList.Flink; ptmr != (PTMR)&TimerList; ptmr = (PTMR)ptmr->TmrList.Flink) {

        if (LOWORD(ptmr->dwEventID) == wIDEvent &&
            ptmr->htask16 == htask16 &&
            (ptmr->hwnd16 == hwnd16 || (hwnd16 && !ptmr->hwnd16))) {

            return ptmr;
        }
    }

    return (PTMR)NULL;
}


//
// Search for a killed timer by its 16-bit information.
//
//
PTMR FindKilledTimer16(HWND16 hwnd16, HTASK16 htask16, WORD wIDEvent)
{
    register PTMR ptmr;

    for (ptmr = (PTMR)TimerList.Flink; ptmr != (PTMR)&TimerList; ptmr = (PTMR)ptmr->TmrList.Flink) {

        if (ptmr->wIndex == 0 &&
            ptmr->htask16 == htask16 &&
            ptmr->hwnd16 == hwnd16 &&
            (LOWORD(ptmr->dwEventID) == wIDEvent || !hwnd16)) {
            // 1. the timer has been killed and
            // 2. the timer is in this task and
            // 3. the hwnds match (both might be 0) and
            // 4. the IDs match, or the hwnds are both 0 (in that case,
            //    IDs are ignored)

            return ptmr;
        }
    }

    return (PTMR)NULL;
}


VOID FreeTimer16(PTMR ptmr)
{
    WOW32ASSERT(ptmr->wIndex == 0 || ptmr == aptmrWOWTimers[ptmr->wIndex]);
    aptmrWOWTimers[ptmr->wIndex] = NULL;
    RemoveEntryList(&ptmr->TmrList);
    free_w(ptmr);
}


VOID DestroyTimers16(HTASK16 htask16)
{
    PTMR ptmr, next;

    for (ptmr = (PTMR)TimerList.Flink; ptmr != (PTMR)&TimerList; ptmr = next) {

        next = (PTMR)ptmr->TmrList.Flink;
        if (ptmr->htask16 == htask16) {

            //
            // don't call KillTimer() if the timer was associated with
            // a window and the window is gone, USER has already
            // cleaned it up.
            //

            if (ptmr == aptmrWOWTimers[ptmr->wIndex] && (!ptmr->hwnd32 || IsWindow(ptmr->hwnd32))) {
                if ( KillTimer(ptmr->hwnd32, ptmr->dwEventID) ) {
                    LOGDEBUG(LOG_IMPORTANT,
                       ("DestroyTimers16:Killed %04x\n",ptmr->dwEventID));
                } else {
                    LOGDEBUG(LOG_ERROR,
                       ("DestroyTimers16:FAILED %04x\n",ptmr->dwEventID));
                }
            }
            FreeTimer16(ptmr);
        }

    }
}


VOID W32TimerFunc(UINT index, HWND hwnd, UINT idEvent, DWORD dwTime)
{
    PARM16 Parm16;
    register PTMR ptmr;

    ptmr = aptmrWOWTimers[index];

    if ( !ptmr ) {
        LOGDEBUG(LOG_ALWAYS,("    W32TimerFunc ERROR: cannot find timer %08x\n", idEvent));
        return;
    }

    if (ptmr->dwEventID != idEvent) {
        //
        // This is an extra timer message which was already in the message
        // queue when the app called KillTimer().  The PTMR isn't in the
        // array, but it is still linked into the TimerList.
        //
        LOGDEBUG(LOG_WARNING,("    W32TimerFunc WARNING: Timer %08x called after KillTimer()\n", idEvent));
        for (ptmr = (PTMR)TimerList.Flink; ptmr != (PTMR)&TimerList; ptmr = (PTMR)ptmr->TmrList.Flink) {
            if (ptmr->dwEventID == idEvent) {
                break;
            }
        }

        if ( ptmr == (PTMR)&TimerList ) {
            LOGDEBUG(LOG_ALWAYS,("    W32TimerFunc ERROR: cannot find timer %08x (second case)\n", idEvent));
            return;
        }
    }

    Parm16.WndProc.hwnd   = ptmr->hwnd16;
    Parm16.WndProc.wMsg   = WM_TIMER;
    Parm16.WndProc.wParam = LOWORD(ptmr->dwEventID);
    Parm16.WndProc.lParam = dwTime;
    Parm16.WndProc.hInst  = 0;     // callback16 defaults to ss

    CallBack16(RET_WNDPROC, &Parm16, ptmr->vpfnTimerProc, NULL);
}


/*++
    BOOL KillTimer(<hwnd>, <nIDEvent>)
    HWND <hwnd>;
    INT <nIDEvent>;

    The %KillTimer% function kills the timer event identified by the <hwnd> and
    <nIDEvent> parameters. Any pending WM_TIMER messages associated with the
    timer are removed from the message queue.

    <hwnd>
        Identifies the window associated with the given timer event. This must
        be the same value passed as the hwnd parameter to the SetTimer function
        call that created the timer event.

    <nIDEvent>
        Specifies the timer event to be killed. If the application called
        %SetTimer% with the <hwnd> parameter set to NULL, this must be the event
        identifier returned by %SetTimer%. If the <hwnd> parameter of %SetTimer%
        was a valid window handle, <nIDEvent> must be the value of the
        <nIDEvent> parameter passed to %SetTimer%.

    The return value specifies the outcome of the function. It is TRUE if the
    event was killed. It is FALSE if the %KillTimer% function could not find the
    specified timer event.
--*/

ULONG FASTCALL WU32KillTimer(PVDMFRAME pFrame)
{
    ULONG ul;
    register PTMR ptmr;
    register PKILLTIMER16 parg16;
    HWND16 hwnd16;
    WORD   wIDEvent;
    HAND16 htask16;

    GETARGPTR(pFrame, sizeof(KILLTIMER16), parg16);

    htask16  = CURRENTPTD()->htask16;
    hwnd16   = (HWND16)parg16->f1;
    wIDEvent = parg16->f2;

    ptmr = IsDuplicateTimer16(hwnd16, htask16, wIDEvent);

    if (ptmr) {
        ul = GETBOOL16(KillTimer(ptmr->hwnd32, ptmr->dwEventID));
        aptmrWOWTimers[ptmr->wIndex] = NULL;
        ptmr->wIndex = 0;
    }
    else {
        ul = 0;
        LOGDEBUG(LOG_IMPORTANT,("    WU32KillTimer ERROR: cannot find timer %04x\n", wIDEvent));
    }

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    WORD SetTimer(<hwnd>, <nIDEvent>, <wElapse>, <lpTimerFunc>)
    HWND <hwnd>;
    int <nIDEvent>;
    WORD <wElapse>;
    FARPROC <lpTimerFunc>;

    The %SetTimer% function creates a system timer event. When a timer event
    occurs, Windows passes a WM_TIMER message to the application-supplied
    function specified by the <lpTimerFunc> parameter. The function can then
    process the event. A NULL value for <lpTimerFunc> causes WM_TIMER messages
    to be placed in the application queue.

    <hwnd>
        Identifies the window to be associated with the timer. If hwnd is NULL,
        no window is associated with the timer.

    <nIDEvent>
        Specifies a nonzero timer-event identifier if the <hwnd> parameter
        is not NULL.

    <wElapse>
        Specifies the elapsed time (in milliseconds) between timer
        events.

    <lpTimerFunc>
        Is the procedure-instance address of the function to be
        notified when the timer event takes place. If <lpTimerFunc> is NULL, the
        WM_TIMER message is placed in the application queue, and the %hwnd%
        member of the %MSG% structure contains the <hwnd> parameter given in the
        %SetTimer% function call. See the following Comments section for
        details.

    The return value specifies the integer identifier for the new timer event.
    If the <hwnd> parameter is NULL, an application passes this value to the
    %KillTimer% function to kill the timer event. The return value is zero if
    the timer was not created.

    Timers are a limited global resource; therefore, it is important that an
    application check the value returned by the %SetTimer% function to verify
    that a timer is actually available.

    To install a timer function, %SetTimer% must receive a procedure-instance
    address of the function, and the function must be exported in the
    application's module-definition file. A procedure-instance address can be
    created using the %MakeProcInstance% function.

    The callback function must use the Pascal calling convention and must be
    declared %FAR%.

    Callback Function:

    WORD FAR PASCAL <TimerFunc>(<hwnd>, <wMsg>, <nIDEvent>, <dwTime>)
    HWND <hwnd>;
    WORD <wMsg>;
    int <nIDEvent>;
    DWORD <dwTime>;

    <TimerFunc> is a placeholder for the application-supplied function name. The
    actual name must be exported by including it in an %EXPORTS% statement in
    the application's module-definition file.

    <hwnd>
        Identifies the window associated with the timer event.

    <wMsg>
        Specifies the WM_TIMER message.

    <nIDEvent>
        Specifies the timer's ID.

    <dwTime>
        Specifies the current system time.
--*/

ULONG FASTCALL WU32SetTimer(PVDMFRAME pFrame)
{
    ULONG ul;
    register PTMR ptmr;
    register PSETTIMER16 parg16;
    HWND16  hwnd16;
    WORD    wIDEvent;
    WORD    wElapse;
    DWORD   vpfnTimerProc;
    DWORD   dwTimerProc32;
    HAND16  htask16;
    INT     iTimer;

    GETARGPTR(pFrame, sizeof(SETTIMER16), parg16);

    ul = 0;

    htask16       = CURRENTPTD()->htask16;
    hwnd16        = (HWND16)parg16->f1;
    wIDEvent      = parg16->f2;
    wElapse       = parg16->f3;

    // Don't allow WOW apps to set a timer with a period of less than
    // 55 ms. Myst and Winstone depend on this.
    if (wElapse < 55) wElapse = 55;

    vpfnTimerProc = VPFN32(parg16->f4);

    ptmr = IsDuplicateTimer16(hwnd16, htask16, wIDEvent);

    if (!ptmr) {

        // Loop through the slots in the timer array

        iTimer = 2;
        while (iTimer < NUMEL(aptmrWOWTimers)) {
            /*
            ** Find a slot in the arrays for which
            ** no pointer has yet been allocated.
            */
            if ( !aptmrWOWTimers[iTimer] ) {

                //
                // See if there is already thunking information for this
                // timer.  If there is, delete it from the list of timer
                // info and re-use its memory because this new timer
                // superceeds the old thunking information.
                //
                ptmr = FindKilledTimer16(hwnd16, htask16, wIDEvent);
                if (ptmr) {

                    RemoveEntryList(&ptmr->TmrList);

                } else {

                    // Allocate a TMR structure for the new timer
                    ptmr = malloc_w(sizeof(TMR));

                }

                aptmrWOWTimers[iTimer] = ptmr;

                if (!ptmr) {
                    LOGDEBUG(LOG_ALWAYS,("    WOW32 ERROR: TMR allocation failure\n"));
                    return 0;
                }

                break;          // Fall out into initialization code
            }
            iTimer++;
        }
        if (iTimer >= NUMEL(aptmrWOWTimers)) {
            LOGDEBUG(LOG_ALWAYS,("    WOW32 ERROR: out of timer slots\n"));
            return 0;
        }

        // Initialize the constant parts of the TMR structure (done on 1st SetTimer)
        InsertHeadList(&TimerList, &ptmr->TmrList);
        ptmr->hwnd16    = hwnd16;
        ptmr->hwnd32    = HWND32(hwnd16);
        ptmr->htask16   = htask16;
        ptmr->wIndex    = (WORD)iTimer;
    }


    // Setup the changeable parts of the TMR structure (done for every SetTimer)

    if (vpfnTimerProc) {
        dwTimerProc32 = (DWORD)afnTimerFuncs[ptmr->wIndex];
    } else {
        dwTimerProc32 = (DWORD)NULL;
    }

    ptmr->vpfnTimerProc = vpfnTimerProc;
    ptmr->dwTimerProc32 = dwTimerProc32;

    ul = SetTimer(
                ptmr->hwnd32,
                (UINT)wIDEvent,
                (UINT)wElapse,
                (TIMERPROC)dwTimerProc32 );

    //
    // USER-generated timerID's are between 0x100 and 0x7fff
    //

    WOW32ASSERT(HIWORD(ul) == 0);

    if (ul) {

        ptmr->dwEventID = ul;

        //
        // when hwnd!=NULL and nEventID==0 the API returns 1 to
        // indicate success but the timer's ID is 0 as requested.
        //

        if (!wIDEvent && ptmr->hwnd32)
            ptmr->dwEventID = 0;

    } else {

        // Since the real SetTimer failed, free
        // our local data using simply our own timer ID

        FreeTimer16(ptmr);
    }

    FREEARGPTR(parg16);
    RETURN(ul);
}


VOID CALLBACK W32Timer1(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(1, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer2(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(2, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer3(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(3, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer4(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(4, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer5(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(5, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer6(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(6, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer7(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(7, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer8(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(8, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer9(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(9, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer10(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(10, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer11(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(11, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer12(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(12, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer13(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(13, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer14(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(14, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer15(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(15, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer16(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(16, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer17(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(17, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer18(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(18, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer19(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(19, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer20(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(20, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer21(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(21, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer22(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(22, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer23(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(23, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer24(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(24, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer25(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(25, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer26(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(26, hwnd, idEvent, dwTime);
}

VOID CALLBACK W32Timer27(HWND hwnd, UINT msg, UINT idEvent, DWORD dwTime)
{
    WOW32ASSERT(msg == WM_TIMER);
    W32TimerFunc(27, hwnd, idEvent, dwTime);
}