/*
 * Misc. USER functions
 *
 * Copyright 1993 Robert J. Amstadt
 *	     1996 Alex Korobka 
 */

#include <stdio.h>
#include <stdlib.h>
#include "windows.h"
#include "resource.h"
#include "heap.h"
#include "gdi.h"
#include "user.h"
#include "task.h"
#include "queue.h"
#include "win.h"
#include "clipboard.h"
#include "hook.h"
#include "debug.h"
#include "toolhelp.h"
#include "message.h"
#include "shell.h"

WORD USER_HeapSel = 0;

extern BOOL32 MENU_PatchResidentPopup( HQUEUE16, WND* );
extern void QUEUE_FlushMessages(HQUEUE16);

/***********************************************************************
 *           GetFreeSystemResources   (USER.284)
 */
WORD WINAPI GetFreeSystemResources( WORD resType )
{
    int userPercent, gdiPercent;

    switch(resType)
    {
    case GFSR_USERRESOURCES:
        userPercent = (int)LOCAL_CountFree( USER_HeapSel ) * 100 /
                               LOCAL_HeapSize( USER_HeapSel );
        gdiPercent  = 100;
        break;

    case GFSR_GDIRESOURCES:
        gdiPercent  = (int)LOCAL_CountFree( GDI_HeapSel ) * 100 /
                               LOCAL_HeapSize( GDI_HeapSel );
        userPercent = 100;
        break;

    case GFSR_SYSTEMRESOURCES:
        userPercent = (int)LOCAL_CountFree( USER_HeapSel ) * 100 /
                               LOCAL_HeapSize( USER_HeapSel );
        gdiPercent  = (int)LOCAL_CountFree( GDI_HeapSel ) * 100 /
                               LOCAL_HeapSize( GDI_HeapSel );
        break;

    default:
        return 0;
    }
    return (WORD)MIN( userPercent, gdiPercent );
}


/***********************************************************************
 *           SystemHeapInfo   (TOOLHELP.71)
 */
BOOL16 WINAPI SystemHeapInfo( SYSHEAPINFO *pHeapInfo )
{
    pHeapInfo->wUserFreePercent = GetFreeSystemResources( GFSR_USERRESOURCES );
    pHeapInfo->wGDIFreePercent  = GetFreeSystemResources( GFSR_GDIRESOURCES );
    pHeapInfo->hUserSegment = USER_HeapSel;
    pHeapInfo->hGDISegment  = GDI_HeapSel;
    return TRUE;
}


/***********************************************************************
 *           TimerCount   (TOOLHELP.80)
 */
BOOL16 WINAPI TimerCount( TIMERINFO *pTimerInfo )
{
    /* FIXME
     * In standard mode, dwmsSinceStart = dwmsThisVM 
     *
     * I tested this, under Windows in enhanced mode, and
     * if you never switch VM (ie start/stop DOS) these
     * values should be the same as well. 
     *
     * Also, Wine should adjust for the hardware timer
     * to reduce the amount of error to ~1ms. 
     * I can't be bothered, can you?
     */
    pTimerInfo->dwmsSinceStart = pTimerInfo->dwmsThisVM = GetTickCount();
    return TRUE;
}

static RESOURCEHANDLER16 __r16loader = NULL;

/**********************************************************************
 *           USER_CallDefaultRsrcHandler
 *
 * Called by the LoadDIBIcon/CursorHandler().
 */
HGLOBAL16 USER_CallDefaultRsrcHandler( HGLOBAL16 hMemObj, HMODULE16 hModule, HRSRC16 hRsrc )
{
    return __r16loader( hMemObj, hModule, hRsrc );
}

/**********************************************************************
 *           USER_InstallRsrcHandler
 */
static void USER_InstallRsrcHandler( HINSTANCE16 hInstance )
{
    FARPROC16 proc;

    /* SetResourceHandler() returns previous function which is set
     * when a module's resource table is loaded. */

    proc = SetResourceHandler( hInstance, RT_ICON, (FARPROC32)LoadDIBIconHandler );
    if(!__r16loader ) 
	__r16loader = (RESOURCEHANDLER16)proc;
    proc = SetResourceHandler( hInstance, RT_CURSOR, (FARPROC32)LoadDIBCursorHandler );
    if(!__r16loader )
	__r16loader = (RESOURCEHANDLER16)proc;
}

/**********************************************************************
 *           InitApp   (USER.5)
 */
INT16 WINAPI InitApp( HINSTANCE16 hInstance )
{
    int queueSize;

      /* InitTask() calls LibMain()'s of implicitly loaded DLLs 
       * prior to InitApp() so there is no clean way to do
       * SetTaskSignalHandler() in time. So, broken Windows bypasses 
       * a pTask->userhandler on startup and simply calls a global 
       * function pointer to the default USER signal handler.
       */

    USER_InstallRsrcHandler( hInstance );

      /* Create task message queue */
    queueSize = GetProfileInt32A( "windows", "DefaultQueueSize", 8 );
    if (!SetMessageQueue32( queueSize )) return 0;

    return 1;
}

/**********************************************************************
 *           USER_ModuleUnload
 */
static void USER_ModuleUnload( HMODULE16 hModule )
{
    HOOK_FreeModuleHooks( hModule );
    CLASS_FreeModuleClasses( hModule );
}

/**********************************************************************
 *           USER_AppExit
 */
static void USER_AppExit( HTASK16 hTask, HINSTANCE16 hInstance, HQUEUE16 hQueue )
{
    /* FIXME: empty clipboard if needed, maybe destroy menus (Windows
     *	      only complains about them but does nothing);
     */

    WND* desktop = WIN_GetDesktop();

    /* Patch desktop window */
    if( desktop->hmemTaskQ == hQueue )
	desktop->hmemTaskQ = GetTaskQueue(TASK_GetNextTask(hTask));

    /* Patch resident popup menu window */
    MENU_PatchResidentPopup( hQueue, NULL );

    TIMER_RemoveQueueTimers( hQueue );

    QUEUE_FlushMessages( hQueue );
    HOOK_FreeQueueHooks( hQueue );

    QUEUE_SetExitingQueue( hQueue );
    WIN_ResetQueueWindows( desktop, hQueue, (HQUEUE16)0);
    CLIPBOARD_ResetLock( hQueue, 0 );
    QUEUE_SetExitingQueue( 0 );

    /* Free the message queue */

    QUEUE_DeleteMsgQueue( hQueue );

    /* ModuleUnload() in "Internals" */

    hInstance = GetExePtr( hInstance );
    if( GetModuleUsage( hInstance ) <= 1 ) 
	USER_ModuleUnload( hInstance );
}


/***********************************************************************
 *           USER_ExitWindows
 *
 * Clean-up everything and exit the Wine process.
 * This is the back-end of ExitWindows(), called when all windows
 * have agreed to be terminated.
 */
void USER_ExitWindows(void)
{
    /* Do the clean-up stuff */

    WriteOutProfiles();
    SHELL_SaveRegistry();

    exit(0);
}


/***********************************************************************
 *           USER_SignalProc (USER.314)
 */
void WINAPI USER_SignalProc( HANDLE16 hTaskOrModule, UINT16 uCode,
                             UINT16 uExitFn, HINSTANCE16 hInstance,
                             HQUEUE16 hQueue )
{
    switch( uCode )
    {
	case USIG_GPF:
	case USIG_TERMINATION:
	     USER_AppExit( hTaskOrModule, hInstance, hQueue ); /* task */
	     break;

	case USIG_DLL_LOAD:
	     USER_InstallRsrcHandler( hTaskOrModule ); /* module */
	     break;

	case USIG_DLL_UNLOAD:
	     USER_ModuleUnload( hTaskOrModule ); /* module */
	     break;

	default:
	     fprintf(stderr,"Unimplemented USER signal: %i\n", (int)uCode );
    }
}


/***********************************************************************
 *           ExitWindows16   (USER.7)
 */
BOOL16 WINAPI ExitWindows16( DWORD dwReturnCode, UINT16 wReserved )
{
    return ExitWindowsEx( EWX_LOGOFF, 0xffffffff );
}


/***********************************************************************
 *           ExitWindowsExec16   (USER.246)
 */
BOOL16 WINAPI ExitWindowsExec16( LPCSTR lpszExe, LPCSTR lpszParams )
{
fprintf(stdnimp, "ExitWindowsExec() : Should run the following in DOS-mode :\n\t\"%s %s\"\n",
	lpszExe, lpszParams);
    return ExitWindowsEx( EWX_LOGOFF, 0xffffffff );
}


/***********************************************************************
 *           ExitWindowsEx   (USER32.195)
 */
BOOL32 WINAPI ExitWindowsEx( UINT32 flags, DWORD reserved )
{
    int i;
    BOOL32 result;
    WND **list, **ppWnd;
        
    /* We have to build a list of all windows first, as in EnumWindows */

    if (!(list = WIN_BuildWinArray( WIN_GetDesktop(), 0, NULL ))) return FALSE;

    /* Send a WM_QUERYENDSESSION message to every window */

    for (ppWnd = list, i = 0; *ppWnd; ppWnd++, i++)
    {
        /* Make sure that the window still exists */
        if (!IsWindow32( (*ppWnd)->hwndSelf )) continue;
	if (!SendMessage16( (*ppWnd)->hwndSelf, WM_QUERYENDSESSION, 0, 0 ))
            break;
    }
    result = !(*ppWnd);

    /* Now notify all windows that got a WM_QUERYENDSESSION of the result */

    for (ppWnd = list; i > 0; i--, ppWnd++)
    {
        if (!IsWindow32( (*ppWnd)->hwndSelf )) continue;
	SendMessage16( (*ppWnd)->hwndSelf, WM_ENDSESSION, result, 0 );
    }
    HeapFree( SystemHeap, 0, list );

    if (result) USER_ExitWindows();
    return FALSE;
}


/***********************************************************************
 *           SetEventHook   (USER.321)
 *
 *	Used by Turbo Debugger for Windows
 */
FARPROC16 SetEventHook(FARPROC16 lpfnEventHook)
{
	fprintf(stderr, "SetEventHook(lpfnEventHook = %08x): stub !\n", (UINT32)lpfnEventHook);
	return NULL;
}
