/*    
	WinNat.c	2.51
    	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 <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "windows.h"
#include "windowsx.h"

#include "Log.h"
#include "kerndef.h"
#include "ModTable.h"
#include "Driver.h"
#include "compat.h"
#include "mfs_config.h"

#include "WinConfig.h"
#include "Kernel.h"
#include "WinMalloc.h"

/* imported stuff */
extern MODULETAB TWIN_ModuleTable[256];

extern DWORD InitTwinDriver(DWORD *);
extern BOOL ReadAppDscrTable(LPSTR);
extern void TWIN_InitLSD();
extern BOOL GdiInitDC(void);
extern void InitSysColors(void);
extern void InitFileSystem(void);
extern DWORD TWIN_DrvCallback(DWORD,LPARAM,LPARAM,LPVOID);
extern short TWIN_TerminateCommdlg( void );

extern void TWIN_InitLog();
BOOL SetSystemMetrics(int, int);
extern int DrvGetCommandLine(int , char ***);

DWORD TWIN_DriverCaps;
#ifdef DRVTAB
TWINDRVSUBPROC **DrvEntryTab;
#else
TWINLIBCALLBACKPROC DrvEntry;
#endif

/* this is exported to people like MFC */
BOOL TWIN_InitSystem(void);

BOOL TWIN_InitializeBinaryCode(void);

/* this is exported to get compatibility */
LPARAM GetCompatibilityFlags(UINT);
LPARAM SetCompatibilityFlags(int);

/* this should be moved down to the driver when we understand the MAC... */
static LPSTR GetCommandLine(int, char **);

int __argc;
char **__argv;

BOOL TWIN_InitDriver()
{
#ifdef DRVTAB
    if ( DrvEntryTab == NULL ) {
	if ( (DrvEntryTab = (TWINDRVSUBPROC **) DriverBootstrap()) == (TWINDRVSUBPROC **)NULL ) {
#else
    if ( DrvEntry == NULL) {
	if ( (DrvEntry = DriverBootstrap()) == NULL) {
#endif
	    ERRSTR((LF_ERR, "No platform-specific driver available.\n"));
	    return FALSE;
	}
	InitFileSystem();

	(void)TWIN_InitSystem();
    }

    return TRUE;
}

extern void InitTwinLibraries(ENTRYTAB *);

extern DWORD PrivateInitDriver(WORD , LPARAM , LPARAM , LPVOID );

int
LoadTwinModDscr(int argc,char **argv, ENTRYTAB *LibInitTab,
		MODULEDSCR *ModDscrApp)
{

    LPSTR lpCmdLine;

    if ( !TWIN_InitDriver() )
	return -1;

    TWIN_DriverCaps = DRVCALL_INITDRIVER(DSUBSYSTEM_GETCAPS,0,0,0);

    InitTwinLibraries(LibInitTab);

    TWIN_ModuleTable[0].flags = 0;      	/* flags for module */
    TWIN_ModuleTable[0].dscr = ModDscrApp;	/* ptr to mod descriptor */

    lpCmdLine = GetCommandLine(argc, argv);

    /* Use argv[0] as the module name, rather than hard coded "module" */
    if( argv != NULL )
      if (argv[0] != NULL)
	    ModDscrApp->name = argv[0];			/* get the executable name */

    return (ReadAppDscrTable(lpCmdLine));

}

static void (*exitfunc)(void);

int
AtExit(void (*func)(void))
{
	exitfunc = func;
	return 0;
}

void WINAPI
FatalExit(int Code)
{
	ExitWindows(Code,0);
}

void
OldExitWindows(void)
{
	ExitWindows(0,0);
}

BOOL WINAPI
ExitWindows(DWORD dwreturn,UINT reserved)
{
	if(exitfunc)
		(*exitfunc)();

	if(reserved == 0) {
#ifdef	LATER
	/* according to definition we have to send WM_QUERYENDSESSION
	   to notify all apps; if all apps agree, send them WM_ENDSESSION
	   before finally shutting down */
#endif
	}

#ifdef WINMALLOC_CHECK
        logstr(LF_LOG,"Delete Stock Objects\n");
	TWIN_DeleteStockObjects ();

        logstr(LF_LOG,"Dumping GDI\n");
	TWIN_DumpGdiObjects ();
#endif

#ifdef CANNOT_DO_THIS
        logstr(LF_LOG,"TerminateDialog\n");
	TWIN_TerminateCommdlg ();
#endif

        logstr(LF_LOG,"Shutdown System\n");
	(void)DRVCALL_INITSYSTEM(DSUBSYSTEM_INIT,0,0,0);

#ifdef WINMALLOC_CHECK
        logstr(LF_LOG,"Cleanup GlobalAlloc\n");
	TWIN_HandleCleanup();

	/* lets dump our memory chain */
        logstr(LF_LOG,"Check Memory Allocation\n");
#ifdef DRIVERMALLOCS
	(void) DrvMallocInfo(0, 0, 0, 0);
#else
	(void) WinMallocInfo(0, 0, 0, 0);
#endif

	/* lets free our memory chain */
	TWIN_FreeAllMemory();

#endif
        LOGSTR((LF_LOG,"Exiting...\n"));
    	exit(dwreturn);
	return(TRUE);   /* Never gets here, but eliminates compiler warning!*/
}

void WINAPI
FatalAppExit(UINT wAction,LPCSTR lpText)
{
    static int flag;

    logstr(LF_ERR,"FatalAppExit: %s\n",lpText);

    /* this is in case of a double fault, do not repeat... */
    if(flag && GetTwinInt(WCP_FATAL)) {
	flag++;
	MessageBox(0,lpText,0,MB_ICONSTOP|MB_OK);
    }

    DeleteTask(0);
}

void
DebugBreak()
{
#if defined(DEBUG) && !defined(TWIN_INTERP_NOT_SUPPORTED)
	extern void debuggerbreak(void);
	debuggerbreak();
#endif
}

WORD
InitApp(HINSTANCE hInstance)
{
    return 1;
}

WORD
WaitEvent(HANDLE taskID)
{
    return 0;
}

BOOL
TWIN_InitSystem(void)
{
    static bInit = FALSE;
    UINT uiCompatibility;
    TWINDRVCONFIG DrvConfig;
    int nScreenWidth,nScreenHeight;

    if (!bInit) {
	/*
	 *  Protect against multiple calls and recursive calls.  Various
	 *  API locations can make a call to us, in addition to the
	 *  normal startup path through main().  This is so that the
	 *  API's that require this initialization can be called from
	 *  constructors in the OWL and MFC libraries.  The API's must
	 *  in turn call us, so make sure we only initialize once.
	 */
	bInit = TRUE;
	TWIN_InitLog();
	
	/* this initializes loadable software devices */
	TWIN_InitLSD();

	/* this gets low-level driver connections etc. */
	uiCompatibility = GetCompatibilityFlags(0);
	DrvConfig.dwDoubleClickTime = (DWORD)GetPrivateProfileInt(
		"windows",
		"DoubleClickRate",
		250,
		GetTwinFilename());
	DrvConfig.lpDrvCallback = &TWIN_DrvCallback;

	if (!DRVCALL_INITSYSTEM(DSUBSYSTEM_INIT,uiCompatibility,1,&DrvConfig))
	    FatalAppExit(0,"Could not initialize driver");

	if (uiCompatibility & WD_VGAMODE)
	    LOGSTR((LF_LOG,"TWIN_InitSystem: default 640x480 screen\n"));
	else {
	    nScreenWidth = GetPrivateProfileInt((LPSTR)"boot",
		"screenwidth",
		DrvConfig.nScreenWidth,
		GetTwinFilename() );
	    if(nScreenWidth == 0)
		nScreenWidth = 640;

	    nScreenHeight = GetPrivateProfileInt((LPSTR)"boot",
		"screenheight",
		DrvConfig.nScreenHeight,
		GetTwinFilename() );

	    if(nScreenHeight == 0)
		nScreenHeight = 480;

	    LOGSTR((LF_LOG,"TWIN_InitSystem: .ini screen dimensions %dx%d\n",
			nScreenWidth,nScreenHeight));

	    /* set system metrics */
	    /* we should set menu height, rather than hardcode at 25 */
	    /* The rule of thumb is MULDIV(fontsize,7,4) 	     */
	    /* Where fontsize is the actual font size, not points    */
	    /* SetSystemMetrics(SM_CYCAPTION,7*12/4); */

	    SetSystemMetrics(SM_CXSCREEN,nScreenWidth);
	    SetSystemMetrics(SM_CYSCREEN,nScreenHeight);
	    SetSystemMetrics(SM_CXFULLSCREEN,nScreenWidth);
	    SetSystemMetrics(SM_CYFULLSCREEN,nScreenHeight-
				GetSystemMetrics(SM_CYCAPTION));
	}

	/* this initializes DC cache */
	GdiInitDC();

	/* this initializes the system color table */
	InitSysColors();

	/* this initializes MFS layer */
	MFS_INIT();

	/* this initializes the binary machinery */
	TWIN_InitializeBinaryCode();
    }
    return bInit;
}

#define	DEFAULT_COMPAT	0

static int Compatibility = -1;

LPARAM
GetCompatibilityFlags(UINT uID)
{
    if ( Compatibility < 0 ) {
    	Compatibility = GetPrivateProfileInt((LPSTR)"boot",
		(LPSTR) "control",
		DEFAULT_COMPAT,
		(LPSTR) GetTwinFilename());
    }

    return (LPARAM)Compatibility;
}

LPARAM
SetCompatibilityFlags(int NewCompatibility)
{
    int OldCompatibility = GetCompatibilityFlags(0);

    if ( NewCompatibility >= 0 )
	Compatibility = NewCompatibility;

    return (LPARAM)OldCompatibility;
}

static LPSTR
GetCommandLine(int argc, char **argv)
{
    int nCount,nLen;
    LPSTR lpCmd;
    
    argc = DrvGetCommandLine(argc, &argv);

    __argc = argc;
    __argv = argv;

    if( argc == 0 )
    {
      return( NULL );
    }

    for (nCount = 0, nLen = 0; nCount < __argc; nCount ++)
	nLen += strlen(__argv[nCount]) + 1;

    lpCmd = WinMalloc(nLen + 1);
    if (__argv[0])
    	strcpy(lpCmd, __argv[0]);
    else
    	lpCmd[0] = '\0';

    if (__argc > 1) {
	for (nCount = 1; nCount < __argc; nCount++) {
	    strcat(lpCmd, " ");
	    strcat(lpCmd, __argv[nCount]);
	}
    }

    return lpCmd;
}
