/*----------------------------------------------------------------------------
 *
 *  main.c
 *  ------
 *
 *  Purpose: Provide the startup processing for VisualAge Smalltalk Enterprise
 *
 *  Commandline switches:
 *     -nosplash  Do not display the splash screen
 *     -singleinstance  Disallow multiple copies of the executable running
 *                at the same time
 *     -no_break  Do not display the User Break button
 *
 *  Licensed Materials - Property of IBM
 *
 *  (C) Copyright International Business Machines Corp., 1994, 2000
 *  All rights reserved
 *
 *  US Government Users Restricted Rights -
 *  Use, duplication, or disclosure restricted
 *  by GSA ADP Schedule Contract with IBM Corp.
 *
 *  Modifications:
 *     5.0.1.0 24Jan00(jok) Update to 5.0.1
 *                          Update parse.c for tenureFailedGCCount option
 *
 *----------------------------------------------------------------------------*/

#define PRODUCT_NAME    "VisualAge Smalltalk Enterprise"
#define PRODUCT_VERSION "Version 5.0.1"
#define PRODUCT_STATUS  ""
#define PRODUCT_EDITION "01/24/2000"
#define PRODUCT_COPYR   "(C) Copyright International Business Machines Corporation  1994, 2000.\nAll Rights Reserved."

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "esrc.h"
#include "esuser.h"

/*--------------------------------------------------------------------------*/
/*      DEBUG support                                                       */
/*--------------------------------------------------------------------------*/

#ifdef DEBUG01
#define dbg_printf( x ) { printf x ; fflush( stdout ); }
#else
#define dbg_printf( x )
#endif

#define QUOTE_CHAR '"'

U_32 WindowsVersion;

static	HINSTANCE	hInstanceCur;
static	char *ES_cmdLineParams[128];

WNDPROC StaticProc;
HBITMAP splashBitmap = (HBITMAP) NULL;
HPALETTE splashPalette = (HPALETTE) NULL;

/* function prototypes */
BOOLEAN EsFindImageFileName(int argc, char *argv[], char *envp[], EsGlobalInfo * info);
BOOLEAN EsParseCommandLineOptions(int argc, char *argv[], char *envp[], EsGlobalInfo * info);
void EsSetGlobalHInstance(ESGlobalInfo *ginfo, HINSTANCE hInst);
extern void EsSplashShutDown(EsGlobalInfo*);
extern EsPrimitiveTable WindowProcPrimitiveTable;

EsDefinePrimitiveTable(EsExePrimitiveTable)
	EsSubTable(WindowProcPrimitiveTable)
EsEndPrimitiveTable



/*
 *	Takes the command line (1 string) and breaks it into an
 *	argv[] style list.
 *	Understands Win95 LFNs and strips quotes off
 *	the exe name and the parameters.
 */

parseWords(char *buffer, char *words[])
{
	int count = 0;

	while (*buffer) {
		if(QUOTE_CHAR == *buffer) {	/* we have a quoted name. */
			words[count++] = ++buffer;	/* move past the quote */
			while(*buffer && QUOTE_CHAR != *buffer)
				buffer++;
			if (*buffer)
				*buffer++ = '\0';	/* slam the close quote and advance */
			}
		else {
			words[count++] = buffer;
			while (*buffer && *buffer != ' ' && *buffer != '\t' && *buffer != '\r')
				buffer++;
			}
		if (*buffer)
			*buffer++ = '\0';
		while (*buffer == ' ' || *buffer == '\t' || *buffer == '\r')
			buffer++;
		}
	return count;
}




HPALETTE CreateDIBPalette (LPBITMAPINFO lpbmi, LPINT lpiNumColors)
{
	LPBITMAPINFOHEADER lpbi;
	LPLOGPALETTE lpPal;
	HANDLE hLogPal;
	HPALETTE hPal = NULL;
	int i;

	lpbi = (LPBITMAPINFOHEADER)lpbmi;
	if (lpbi->biBitCount <= 8)
		*lpiNumColors = (1 << lpbi->biBitCount);
	else
		*lpiNumColors = 0;  // No palette needed for 24 BPP DIB

	if (*lpiNumColors) {
		hLogPal = GlobalAlloc (GHND, sizeof (LOGPALETTE) +
							   sizeof (PALETTEENTRY) * (*lpiNumColors));
		lpPal = (LPLOGPALETTE) GlobalLock (hLogPal);
		lpPal->palVersion    = 0x300;
		lpPal->palNumEntries = *lpiNumColors;

		for (i = 0;  i < *lpiNumColors;  i++) {
			lpPal->palPalEntry[i].peRed   = lpbmi->bmiColors[i].rgbRed;
			lpPal->palPalEntry[i].peGreen = lpbmi->bmiColors[i].rgbGreen;
			lpPal->palPalEntry[i].peBlue  = lpbmi->bmiColors[i].rgbBlue;
			lpPal->palPalEntry[i].peFlags = 0;
		}
		hPal = CreatePalette (lpPal);
		GlobalUnlock (hLogPal);
		GlobalFree   (hLogPal);
	}
	return hPal;
}



HBITMAP LoadResourceBitmap(HINSTANCE hInstance, LPSTR lpString, HPALETTE FAR* lphPalette)
{
	HRSRC  hRsrc;
	HGLOBAL hGlobal;
	HBITMAP hBitmapFinal = NULL;
	LPBITMAPINFOHEADER  lpbi;
	HDC hdc;
	int iNumColors;

	hRsrc = FindResource(hInstance, lpString, RT_BITMAP);
	if (hRsrc)
	{
		hGlobal = LoadResource(hInstance, hRsrc);
		lpbi = (LPBITMAPINFOHEADER)LockResource(hGlobal);

		hdc = GetDC(NULL);
		*lphPalette =  CreateDIBPalette ((LPBITMAPINFO)lpbi, &iNumColors);
		if (*lphPalette)
		{
			SelectPalette(hdc,*lphPalette,FALSE);
			RealizePalette(hdc);
		}

		hBitmapFinal = CreateDIBitmap(hdc,
									  (LPBITMAPINFOHEADER)lpbi,
									  (LONG)CBM_INIT,
									  (LPSTR)lpbi + lpbi->biSize + iNumColors *
									  sizeof(RGBQUAD),
									  (LPBITMAPINFO)lpbi,
									  DIB_RGB_COLORS );
		ReleaseDC(NULL,hdc);
		UnlockResource(hGlobal);
		FreeResource(hGlobal);
	}
	return (hBitmapFinal);
}



LRESULT CALLBACK DrawBitmapProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{				
	HBITMAP hOldBitmap;
	HDC hMemDC;
	BITMAP bm;
	PAINTSTRUCT ps;

	if (splashBitmap) {
		switch(msg) {
			case WM_PAINT:
				BeginPaint (hwnd, &ps);
				GetObject(splashBitmap, sizeof(BITMAP), (LPSTR)&bm);
				hMemDC = CreateCompatibleDC(ps.hdc);
				SelectPalette(ps.hdc,splashPalette,FALSE);
				RealizePalette(ps.hdc);
				SelectPalette(hMemDC,splashPalette,FALSE);
				RealizePalette(hMemDC);
				hOldBitmap = SelectObject(hMemDC,splashBitmap);
				BitBlt(ps.hdc,0,0,bm.bmWidth,bm.bmHeight,hMemDC,0,0,SRCCOPY);
				SelectObject(hMemDC,hOldBitmap);
				DeleteDC(hMemDC);
				EndPaint (hwnd, &ps);
				return (LRESULT) 0;
				break;
		   default:
			   break;
		}
	}
	return (LRESULT) CallWindowProc (StaticProc, hwnd, msg, wParam, lParam);
}



BOOL CALLBACK DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	int displayHeight, displayWidth, x, y;
	HDC hdc;
	BITMAP bm;

	switch(uMsg) {
		case WM_INITDIALOG: {
			if (splashBitmap)
				GetObject(splashBitmap, sizeof(BITMAP), (LPSTR)&bm);
			displayHeight = GetSystemMetrics( SM_CYSCREEN );
			displayWidth = GetSystemMetrics( SM_CXSCREEN );
			hdc = GetDC(hwndDlg);
			x = max( (displayWidth - bm.bmWidth) / 2, 0 );
			y = max( (displayHeight - bm.bmHeight) / 2, 0 );
			MoveWindow( hwndDlg, x, y, bm.bmWidth, bm.bmHeight, TRUE );
			ReleaseDC(hwndDlg, hdc);
			return TRUE;
			break;

		case WM_CLOSE:
			DestroyWindow(hwndDlg);
			return TRUE;
			break;
			
		case WM_DESTROY:
			PostQuitMessage(0);
			return TRUE;
			break;
		}
	}
	return FALSE;
}



static void splashDialogThread(EsGlobalInfo *globalInfo)
{
	HWND hwndStatic;
	HWND hwndDlgBox;
	MSG msg;

	splashBitmap = LoadResourceBitmap(hInstanceCur,"APP_SPLASH",&splashPalette);
	hwndDlgBox = CreateDialog(hInstanceCur, "IDD_SPLASHDIALOG", NULL, DlgProc );
	hwndStatic = GetDlgItem (hwndDlgBox, IDBM_SPLASHBITMAP);
	StaticProc = (WNDPROC) GetWindowLong (hwndStatic, GWL_WNDPROC);
	SetWindowLong (hwndStatic, GWL_WNDPROC, (LONG) DrawBitmapProc);

	ShowWindow( hwndDlgBox, SW_SHOW );
	UpdateWindow( hwndDlgBox );

	/* Initialize handles to the splash screen */
	globalInfo->splashDialogHandle = hwndDlgBox;
	globalInfo->splashStatusLineHandle = NULL; /* GetDlgItem (hwndDlgBox , IDS_STATUS); */

	/* Set the desired level of messaging */
	globalInfo->messageLevel = ESVM_MSG_LEVEL_NONE;

	while (GetMessage( &msg, NULL, 0L, 0L )) {
	    DispatchMessage( &msg );
	}

	globalInfo->splashDialogHandle = NULL;
	globalInfo->splashStatusLineHandle = NULL;
	
	if (splashBitmap)
		DeleteObject(splashBitmap);
	if (splashPalette)
		DeleteObject(splashPalette);
}



U_32 GetPlatform(void)
{
	OSVERSIONINFO osVerInfo;

	osVerInfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);

	if(!GetVersionEx(&osVerInfo))
		return VER_PLATFORM_WIN32s;

	return osVerInfo.dwPlatformId;
}



EsMain(int argc, char ** argv, char ** envp)
{
	I_32 rc, i;
	EsGlobalInfo *globalInfo;
	DWORD dwTid;
	HANDLE hThread;
	BOOLEAN splashEnabled = TRUE;
	BOOLEAN mutexEnabled = FALSE;
	void *startupMutex;
	char mutexNameBuffer[1024], *exeName, *exeExtension, *ptr;

	/* Walk the command line looking VA specific command line options */
   dbg_printf( ("Entering main, %d commandline arguements\n", argc) );
	for(i=1; i < argc; i++) {
      dbg_printf( ("\t%s\n", argv[i]) );
		if(strcmp(argv[i],"-nosplash") == 0)
			splashEnabled = FALSE;
		if(strcmp(argv[i],"-singleinstance") == 0)
			mutexEnabled = TRUE;
	}
   dbg_printf( ("splashEnabled: %d\n", splashEnabled) );
   dbg_printf( ("mutexEnabled: %d\n", mutexEnabled) );
	
	/* single start mutex acquisition */
	if(mutexEnabled) {
		/* strip the path name and extension from the file */
		exeName = strrchr(argv[0], '\\');
		if(exeName)
			exeName++;
		else
			exeName = argv[0];

		strcpy(mutexNameBuffer, exeName);
		for (ptr = mutexNameBuffer; *ptr; ptr++)
			*ptr = toupper(*ptr);
		exeExtension = strrchr(mutexNameBuffer, '.');
		if (exeExtension && 0 == strcmp(exeExtension, ".EXE"))
			*exeExtension = '\0';
		strcat(mutexNameBuffer, "_SINGLE_START_MUTEX");

		SetLastError(0);	/* reset the Win32 error status */
		startupMutex = CreateMutex(NULL, FALSE, mutexNameBuffer);
		if(ERROR_ALREADY_EXISTS == GetLastError()) {	/* mutex already exists */
			CloseHandle(startupMutex);
			return 0;
		}
	}

	WindowsVersion = GetPlatform();
	globalInfo = EsInitializeImage();
	if (globalInfo == NULL) {
		EsReportError(EsPrimErrNotEnoughMemory, globalInfo);
		if (mutexEnabled) CloseHandle(startupMutex);
		return EsPrimErrNotEnoughMemory;
	}
	globalInfo->imagePrimitives = (U_32) EsExePrimitiveTable;


	/* bring up the splash dialog, need globalInfo in hand first */
	if (splashEnabled) {
		hThread = CreateThread(
						   NULL,
						   0,	
						   (LPTHREAD_START_ROUTINE) splashDialogThread,
						   globalInfo,
						   0,
						   &dwTid);
		CloseHandle(hThread);
	}


	EsSetGlobalHInstance(globalInfo, hInstanceCur);
			
	if ((rc = EsInitializeTargetInterface(argc, argv, envp, globalInfo)) != EsPrimErrNoError) {
		EsSplashShutDown(globalInfo);
		EsReportError(rc, globalInfo);
		EsShutdownImage(globalInfo);
		if (mutexEnabled)
			CloseHandle(startupMutex);
		return rc;
	}

	EsPrintf ("\n%s, %s %s - %s\nVM Timestamp: %s\n%s\n",
			PRODUCT_NAME,PRODUCT_VERSION,PRODUCT_STATUS,PRODUCT_EDITION,
			EsVMVersionString(),
			PRODUCT_COPYR,
			0,0,0,0);

	if(!EsParseCommandLineOptions(argc, argv, envp, globalInfo)) {
		EsSplashShutDown(globalInfo);
		EsReportError(EsPrimErrImageFileOpenError, globalInfo);
		EsShutdownImage(globalInfo);
		if (mutexEnabled)
			CloseHandle(startupMutex);
		return EsPrimErrImageFileOpenError;
	}

	rc = EsLoadFileImage(argc, argv, envp, globalInfo);

	if (rc == EsPrimErrNoError)
		rc = EsExecuteImage(globalInfo);

	if (rc != EsPrimErrNoError) {
		if (rc < 9000 || (rc > 9100))
			EsReportError (rc, globalInfo);
	}
	

	EsShutDownTargetInterface (globalInfo);
	EsShutdownImage(globalInfo);
	if (mutexEnabled)
		CloseHandle(startupMutex);
	return rc;
}



WINAPI WinMain(
	HINSTANCE hInstCurrent,
	HINSTANCE hInstPrevious,
	LPSTR lpszCmdLine,
	int nCmdShow)
{
	int nArgs, rc;
	void EsSetHInstance(HINSTANCE);
	
	hInstanceCur = hInstCurrent;
	EsSetHInstance(hInstCurrent);
	nArgs = parseWords(GetCommandLine(), &ES_cmdLineParams[0]);

	rc = EsMain(nArgs, ES_cmdLineParams, NULL);
	return rc;
}
