/*    
	SysMisc.c	1.12
    	Copyright 1997 Willows Software, Inc. 

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA.


For more information about the Willows Twin Libraries.

	http://www.willows.com	

To send email to the maintainer of the Willows Twin Libraries.

	mailto:twin@willows.com 

 */
#include <string.h>
#include "windows.h"

#include "Log.h"
#include "kerndef.h"
#include "Kernel.h"
#include "Driver.h"

/* exported stuff */
BOOL TWIN_GetTimerMsg(HWND, HTASK, LPMSG, UINT);
int TWIN_GetTimeout(int);

/* internal routines */
static UINT SetTimer2(HWND, UINT, UINT, TIMERPROC, BOOL);
static BOOL KillTimer2(HWND, UINT, BOOL);

UINT WINAPI
GetDoubleClickTime(void)
{
    UINT uiRet;

    uiRet = (UINT)DRVCALL_EVENTS(PEH_DBLCLKTIME,0,0,0);

    LOGSTR((LF_API,"GetDoubleClickTime: return %x\n",uiRet));

    return uiRet;
}

void WINAPI
SetDoubleClickTime(UINT uInterval)
{
    LOGSTR((LF_API,"SetDoubleClickTime: setting to %x\n",uInterval));

    (void)DRVCALL_EVENTS(PEH_DBLCLKTIME,1,uInterval,0);
}

BOOL WINAPI
SwapMouseButton(BOOL fSwap)
{
    LOGSTR((LF_API,"SwapMouseButton: swap %x\n",fSwap));

    return (BOOL)DRVCALL_EVENTS(PEH_MOUSEBUTTON,fSwap,0,0);
}

typedef struct tagSYNCTIMER
{
    HTASK	hTask;
    HWND	hWnd;
    UINT	uID;
    UINT	uTimeout;
    UINT	uFlags;
    int		nWaitTime;
    TIMERPROC	lpfnTimerProc;
} SYNCTIMER;
typedef SYNCTIMER *LPSYNCTIMER;

#define	STF_SYSTEM	0x0001

#define	SYNC_TIMERS	48
static SYNCTIMER SyncTimers[SYNC_TIMERS];

UINT WINAPI
SetTimer(HWND hWnd, UINT idTimer, UINT uTimeout, TIMERPROC tmprc)
{
    return SetTimer2(hWnd,idTimer,uTimeout,tmprc,FALSE);
}

UINT WINAPI
SetSystemTimer(HWND hWnd, UINT idTimer, UINT uTimeout, TIMERPROC tmprc)
{
    return SetTimer2(hWnd,idTimer,uTimeout,tmprc,TRUE);
}

static int nMaxTimer = 0;

static UINT
SetTimer2(HWND hWnd, UINT idTimer, UINT uTime,
		TIMERPROC tmprc, BOOL bSystem)
{
    LPSYNCTIMER lpSyncTimer;
    HTASK hTask;
    int n;
    UINT uTimeout = (uTime)?uTime:20;

    APISTR((LF_API,"SetTimer: hWnd %x, id %x, timeout %x, proc %x\n",
	hWnd,idTimer,uTimeout,tmprc));

    if (hWnd != 0 && IsWindow(hWnd))
	hTask = GetWindowTask(hWnd);
    else
	hTask = GetCurrentTask();

    for (n = 0; n < SYNC_TIMERS; n++)
	if (SyncTimers[n].hTask == 0) {
	    lpSyncTimer = &SyncTimers[n];
	    lpSyncTimer->hTask = hTask;
	    lpSyncTimer->hWnd = hWnd;
	    lpSyncTimer->uID = idTimer;
	    lpSyncTimer->uTimeout = uTimeout;
	    lpSyncTimer->uFlags = (bSystem)?STF_SYSTEM:0;
	    lpSyncTimer->nWaitTime = (int)uTimeout;
	    lpSyncTimer->lpfnTimerProc = tmprc;
	    LOGSTR((LF_LOG,"SetTimer: returning slot %x\n",n));
	    if (hWnd == 0)
		lpSyncTimer->uID = n + 1;
	    if (n > nMaxTimer)
		nMaxTimer = n;
	    return (lpSyncTimer->uID) ? lpSyncTimer->uID : 1;
	}

    return 0;
}

BOOL WINAPI
KillTimer(HWND hWnd, UINT uID)
{
    return KillTimer2(hWnd,uID,FALSE);
}

BOOL WINAPI
UserKillSystemTimer(HWND hWnd, UINT uID)
{
    return KillTimer2(hWnd,uID,TRUE);
}

static BOOL
KillTimer2(HWND hWnd, UINT uID, BOOL bSystem)
{
    HTASK hTask;
    int n;

    APISTR((LF_API,"KillTimer: hWnd %x, id %x\n",hWnd,uID));

    if (hWnd != 0 && IsWindow(hWnd))
	hTask = GetWindowTask(hWnd);
    else
	hTask = GetCurrentTask();

    for (n = 0; n < SYNC_TIMERS; n++)
	if (SyncTimers[n].hTask == hTask) {
	    if (SyncTimers[n].hWnd == hWnd && SyncTimers[n].uID == uID) {
		SyncTimers[n].hTask = 0;
		if (nMaxTimer && n == nMaxTimer) {
		    nMaxTimer--;
		    while (nMaxTimer && SyncTimers[nMaxTimer].hTask == 0)
			nMaxTimer--;
		}
		return TRUE;
	    }
	}

    return FALSE;
}

BOOL
TWIN_GetTimerMsg(HWND hWnd, HTASK hTask, LPMSG lpMsg, UINT uFlg)
{
    static int nOffset = 0;
    int n,nIndex;
    LPSYNCTIMER lpSyncTimer;
    BOOL bSystem;

    LOGSTR((LF_LOG,"TWIN_GetTimerMsg: hWnd %x task %x\n",hWnd,hTask));

    for (n = 0; n <= nMaxTimer; n++) {
	nIndex = (n+nOffset) % (nMaxTimer+1);
	if (SyncTimers[nIndex].hTask == 0)
	    continue;
	lpSyncTimer = &SyncTimers[nIndex];
	if (lpSyncTimer->hTask == hTask && lpSyncTimer->nWaitTime <= 0) {
	    if (hWnd == 0 || lpSyncTimer->hWnd == hWnd) {
		bSystem = (lpSyncTimer->uFlags & STF_SYSTEM)?TRUE:FALSE;
		LOGSTR((LF_LOG,"returning %s at %d interval %d\n",
			(bSystem)?"WM_SYSTIMER":"WM_TIMER",
			nIndex,lpSyncTimer->nWaitTime));
		LOGSTR((LF_LOG,"timer0 %d\n",SyncTimers[0].nWaitTime));
		LOGSTR((LF_LOG,"timer1 %d\n",SyncTimers[1].nWaitTime));
#ifdef	DEBUG
		memset((LPSTR)lpMsg,'\0',sizeof(MSG));
#endif
		lpMsg->hwnd = lpSyncTimer->hWnd;
		lpMsg->message = (bSystem)?WM_SYSTIMER:WM_TIMER;
		lpMsg->wParam = (WPARAM)lpSyncTimer->uID;
		lpMsg->lParam = (LPARAM)lpSyncTimer->lpfnTimerProc;
		if (uFlg & PM_REMOVE)
		    lpSyncTimer->nWaitTime = (int)lpSyncTimer->uTimeout;
		nOffset = ++nOffset % (nMaxTimer+1);
		return TRUE;
	    }
	}
    }
    QueueClearFlags(hTask, QFTIMER);
    LOGSTR((LF_LOG,"found no timers\n"));
    nOffset = ++nOffset % (nMaxTimer+1);
    return FALSE;
}

int
TWIN_GetTimeout(int nTimeout)
{
    int n;
    int nMinTimeout = 0;
    LPSYNCTIMER lpSyncTimer;

    LOGSTR((LF_LOG,"TWIN_GetTimeout: called with timeout %d\n",nTimeout));

    for (n = 0; n < SYNC_TIMERS; n++) {
	lpSyncTimer = &SyncTimers[n];
	if (lpSyncTimer->hTask == 0)
	    continue;
#ifdef	LATER
	/* sol.exe timers get killed on menu select if we test for 0 here */
	if (lpSyncTimer->nWaitTime == 0)
	    continue;
#endif

	lpSyncTimer->nWaitTime -= nTimeout;

	if (lpSyncTimer->nWaitTime <= 0) {
	    LOGSTR((LF_LOG,"TWIN_GetTimeout: timer %d task %x expired\n",
		n,lpSyncTimer->hTask));
	    QueueSetFlags(lpSyncTimer->hTask,QFTIMER);
	    lpSyncTimer->nWaitTime = 0;
	    nMinTimeout = -1;
	    continue;
	}

	if (nMinTimeout == 0 || lpSyncTimer->nWaitTime < nMinTimeout)
	    nMinTimeout = lpSyncTimer->nWaitTime;

	LOGSTR((LF_LOG,"TWIN_GetTimeout: timeout timer %d, minimum %d\n",
	    n,nMinTimeout));
    }

    LOGSTR((LF_LOG,"TWIN_GetTimeout: returning timeout %d\n",nMinTimeout));

    return nMinTimeout;
}

DWORD WINAPI
GetCurrentTime()
{
    return DRVCALL_EVENTS(PEH_SYSTIME,0,0,0);
}

DWORD WINAPI
GetTickCount()
{
    return DRVCALL_EVENTS(PEH_SYSTIME,0,0,0);
}
