/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001  Ludvig Strigeus
 * Copyright (C) 2001-2006 The ScummVM project
 * Copyright (C) 2002-2006 Chris Apers - PalmOS Backend
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program 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 General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL$
 * $Id$
 *
 */

#include <PalmOS.h>
#include "StarterRsc.h"

#include "games.h"
#include "start.h"
#include "rumble.h"
#include "extend.h"
#include "globals.h"
#include "features.h"
#include "formUtil.h"
#include "formCards.h"
#include "palmdefs.h"

#include "init_palmos.h"
#include "init_stuffs.h"

#include "modules.h"
#include "args.h"


#define BUILD_ERROR(m)	\
	{	StrCopy(msg, m);	\
		StrCat(msg, "\n\nPlease check that all required files are installed on your card, and you have enough free storage memory.");	\
		goto onError; }

#define BUILD_FILE(h,m)	\
	StrCopy(filename, "/Palm/Programs/ScummVM/Mods/");	\
	StrCat(filename, h);	\
	StrCat(filename, m);

#define CHECK_FILE(m) \
	e = VFSFileOpen(volRefNum, filename, vfsModeRead, &file);	\
	if (e)	\
		BUILD_ERROR(m)	\
	else	\
		VFSFileClose(file);

#define IMPRT_FILE(m) \
	e = VFSImportDatabaseFromFile(volRefNum, filename, &cardNo, &dbID);	\
	if (e)	\
		BUILD_ERROR(m)

#define DELET_FILE(f) \
	del_dbID = DmFindDatabase(0, f);	\
	if (del_dbID)	\
		DmDeleteDatabase(0, del_dbID);

void ModDelete() {
	LocalID del_dbID;

	DELET_FILE("Glbs::Common");
	DELET_FILE("Glbs::Engine");
	DELET_FILE("ScummVM-Engine");
}

static void ModSetStack(UInt32 newSize, UInt16 cardNo, LocalID dbID) {
	DmOpenRef dbRef = DmOpenDatabase(cardNo, dbID, dmModeReadWrite);

	if (dbRef) {
		MemHandle pref = DmGetResource('pref',0);
		UInt32 size = 0;
		
		if (pref) {
			SysAppPrefsType *data = (SysAppPrefsType *)MemHandleLock(pref);
			size = data->stackSize;

			if (newSize) {
				SysAppPrefsType newData;
				MemMove(&newData, data, sizeof(SysAppPrefsType));
				newData.stackSize = newSize;
				DmWrite(data, 0, &newData, sizeof(SysAppPrefsType));
			}

			MemPtrUnlock(data);
			DmReleaseResource(pref);
		}

		DmCloseDatabase(dbRef);
	}
}

static Err ModImport(UInt16 volRefNum, UInt8 engine, Boolean *armP) {
#ifndef _DEBUG_ENGINE
	char filename[256];
	UInt16 cardNo;
	LocalID dbID;
	UInt32 result;
	FileRef file;
#endif
	char msg[256];
	FormPtr ofmP, frmP;
	Err e = errNone;

	ofmP = FrmGetActiveForm();
	frmP = FrmInitForm(ImportForm);
	FrmSetActiveForm(frmP);
	FrmDrawForm(frmP);

	// In debug mode, the engine files are directly uploaded to the simulator
#ifndef _DEBUG_ENGINE
	// engine file ?
	BUILD_FILE(engines[engine].fileP, ".engine");
	CHECK_FILE("ScummVM engine file was not found !");
	IMPRT_FILE("Cannot import engine file !");

	// need more files ?
	dbID = DmFindDatabase(0, "ScummVM-Engine");	// be sure to have the correct dbID
	e = SysAppLaunch(cardNo, dbID, 0, sysAppLaunchCustomEngineGetInfo, 0, &result);
	*armP = ((result & GET_MODEARM) == GET_MODEARM);

	// common file ?
	if (!e && (result & GET_DATACOMMON)) {
		BUILD_FILE("common", ".data");
		CHECK_FILE("Common data file was not found !");
		IMPRT_FILE("Cannot import common data file !");
	}
	// data file ?
	if (!e && (result & GET_DATAENGINE)) {
		BUILD_FILE(engines[engine].fileP, ".data");
		CHECK_FILE("Engine data file was not found !");
		IMPRT_FILE("Cannot import engine data file !");
	}
#endif
	// if error, cleanup
	if (e) ModDelete();

onError:
	FrmEraseForm(frmP);
	FrmDeleteForm(frmP);
	if (e) {
		if (ofmP) FrmSetActiveForm(ofmP);
		FrmCustomAlert(FrmErrorAlert, msg, 0, 0);
	}

	return e;
}

#undef DELET_FILE
#undef CHECK_FILE
#undef BUILD_FILE

Boolean StartScummVM() {
	Char **argvP;
	UInt8 lightspeed, argc	= 0;
	UInt32 stackSize;
	Boolean toLauncher, direct, isARM;
	UInt8 engine;
	Char num[6];

	UInt16 index = GamGetSelected();

	argvP = ArgsInit();
	direct = false;

	// start command line (exec name)
	ArgsAdd(&argvP[argc], "-", NULL, &argc);

	// no game selected
	if (index == dmMaxRecordIndex) {
		ListPtr listP;
		UInt16 whichButton;
		
		// init form
		FormPtr frmP = FrmInitForm(EngineForm);
		listP = (ListType *)FrmGetObjectPtr(frmP, FrmGetObjectIndex(frmP, EngineListList));
		itemsText = (Char **)MemPtrNew(ENGINE_COUNT * sizeof(Char *));
		
		for (int i = 0; i < ENGINE_COUNT; i++)
			itemsText[i] = (Char *)engines[i].nameP;
			
		LstSetListChoices (listP, itemsText, ENGINE_COUNT);
		LstSetSelection(listP, 0);	
		
		whichButton = FrmDoDialog(frmP);
		engine = LstGetSelection(listP);

		FrmDeleteForm(frmP);
		MemPtrFree(itemsText);
		itemsText = NULL;
		
		if (whichButton == EngineCancelButton) {
			if (bDirectMode) {
				// and force exit if nothing selected
				EventType event;
				event.eType = keyDownEvent;
				event.data.keyDown.chr = vchrLaunch;
				event.data.keyDown.modifiers = commandKeyMask;
				EvtAddUniqueEventToQueue(&event, 0, true);
			}
			// free args
			ArgsFree(argvP);
			return false;
		}

		// default values
		if (bDirectMode)
			gPrefs->card.volRefNum = parseCards();	// always use the first removable card available (?)
		gVars->filter		= true;
		gVars->palmVolume	= 50;
		gVars->fmQuality	= FM_QUALITY_INI;
		direct = true;

	// somthing selected
	} else {
		Char pathP[256];
		MemHandle recordH;
		GameInfoType *gameInfoP;

		recordH = DmQueryRecord(gameDB,index);
		gameInfoP = (GameInfoType *)MemHandleLock(recordH);
		engine = gameInfoP->engine;

		// build path
		StrCopy(pathP,"/Palm/Programs/ScummVM/Games/");
		if (gameInfoP->pathP[0] == '/')
			StrCopy(pathP, gameInfoP->pathP);
		else if (!(gameInfoP->pathP[0] == '.' && StrLen(gameInfoP->pathP) == 1))
			StrCat(pathP, gameInfoP->pathP);

		// path
		ArgsAdd(&argvP[argc], "-p", pathP, &argc);

		// language
		if (gameInfoP->language > 0) {
			const Char *lang = "zh\0cz\0gb\0en\0fr\0de\0hb\0it\0jp\0kr\0pl\0pt\0ru\0es\0se\0";
			ArgsAdd(&argvP[argc], "-q", (lang + (gameInfoP->language - 1) * 3), &argc);
		}

		// fullscreen ?
		if (gameInfoP->fullscreen)
			ArgsAdd(&argvP[argc], "-f", NULL, &argc);

		// aspect-ratio ?
		ArgsAdd(&argvP[argc], (gameInfoP->aspectRatio ? "--aspect-ratio" : "--no-aspect-ratio"), NULL, &argc);

		// gfx mode
		gVars->filter = gameInfoP->filter;

		switch (gameInfoP->renderMode) {
			case 1:
				ArgsAdd(&argvP[argc], "--render-mode=", "amiga", &argc);
				break;
			case 2:
				ArgsAdd(&argvP[argc], "--render-mode=", "cga", &argc);
				break;
			case 3:
				ArgsAdd(&argvP[argc], "--render-mode=", "ega", &argc);
				break;
			case 4:
				ArgsAdd(&argvP[argc], "--render-mode=", "hercAmber", &argc);
				break;
			case 5:
				ArgsAdd(&argvP[argc], "--render-mode=", "hercGreen", &argc);
				break;
		}		

		switch (gameInfoP->gfxMode)	{
			case 1:
				ArgsAdd(&argvP[argc], "-g", "wide", &argc);
				break;
			default:
				ArgsAdd(&argvP[argc], "-g", "1x", &argc);
				break;
		}

		// load state
		if (gameInfoP->autoLoad) {
			StrIToA(num, gameInfoP->loadSlot);
			ArgsAdd(&argvP[argc], "-x", num, &argc);
		}
		// boot script parameter
		if (gameInfoP->bootParam) {
			StrIToA(num, gameInfoP->bootValue);
			ArgsAdd(&argvP[argc], "-b", num, &argc);
		}
		// not a PC version
		if (gameInfoP->setPlatform) {
			static const char *platform[] = {
				"3do",
				"acorn",
				"amiga",
				"atari",
				"c64",
				"pc",
				"fmtowns",
				"linux",
				"mac",
				"nes",
				"segacd",
				"windows"
			};
			ArgsAdd(&argvP[argc], "--platform=", platform[gameInfoP->platform], &argc);	
		}

		// subtitles
		if (gameInfoP->subtitles)
			ArgsAdd(&argvP[argc],  "-n", NULL, &argc);

		// multi midi ?
		if (gameInfoP->musicInfo.sound.multiMidi)
			ArgsAdd(&argvP[argc], "--multi-midi", NULL, &argc);

		if (engine == ENGINE_SCUMM) {
			// music tempo
			StrIToA(num, gameInfoP->musicInfo.sound.tempo);
			ArgsAdd(&argvP[argc], "--tempo=", num, &argc);
		}

		// talk speed
		if (gameInfoP->talkSpeed) {
			StrIToA(num, gameInfoP->talkValue);
			ArgsAdd(&argvP[argc], "--talkspeed=", num, &argc);
		}

		// music driver
		if (gameInfoP->musicInfo.sound.music) {
			switch (gameInfoP->musicInfo.sound.drvMusic) {
				case 0:	// NULL
					ArgsAdd(&argvP[argc], "-e", "null", &argc);
					break;

				case 1: // AdLib
					ArgsAdd(&argvP[argc], "-e", "adlib", &argc);
					break;

				case 2: // FM Towns
					ArgsAdd(&argvP[argc], "-e", "towns", &argc);
					break;

				case 3: // IBM PCjr
					ArgsAdd(&argvP[argc], "-e", "pcjr", &argc);
					break;

				case 4:	// built-in MIDI
					if (OPTIONS_TST(kOptDeviceZodiac))
						ArgsAdd(&argvP[argc], "-e", "zodiac", &argc);	// Tapwave Zodiac
					else if (OPTIONS_TST(kOptSonyPa1LibAPI))
						ArgsAdd(&argvP[argc], "-e", "ypa1", &argc);		// Pa1Lib devices
					else
						ArgsAdd(&argvP[argc], "-e", "null", &argc);		// error, no music driver
					break;

				case 5: // PC Speaker
					ArgsAdd(&argvP[argc], "-e", "pcspk", &argc);
					break;
			}		

			// output rate
			UInt32 rates[] = {4000, 8000, 11025, 22050, 44100};
			StrIToA(num, rates[gameInfoP->musicInfo.sound.rate]);
			ArgsAdd(&argvP[argc], "--output-rate=", num, &argc);

			// FM quality
			gVars->fmQuality = gameInfoP->fmQuality;

		} else {
			ArgsAdd(&argvP[argc], "-e", "null", &argc);
		}

		// volume control
		StrIToA(num, gameInfoP->musicInfo.volume.sfx);
		ArgsAdd(&argvP[argc], "-s", num, &argc);
		StrIToA(num, gameInfoP->musicInfo.volume.music);
		ArgsAdd(&argvP[argc], "-m", num, &argc);
		StrIToA(num, gameInfoP->musicInfo.volume.speech);
		ArgsAdd(&argvP[argc], "-r", num, &argc);

		// game name
		ArgsAdd(&argvP[argc], gameInfoP->gameP, NULL, &argc);

		// others globals data
		gVars->CD.enable = gameInfoP->musicInfo.sound.CD;
		gVars->CD.driver = gameInfoP->musicInfo.sound.drvCD;
		gVars->CD.format = gameInfoP->musicInfo.sound.frtCD;
		gVars->CD.volume = gameInfoP->musicInfo.volume.audiocd;
		gVars->CD.defaultTrackLength = gameInfoP->musicInfo.sound.defaultTrackLength;
		gVars->CD.firstTrack = gameInfoP->musicInfo.sound.firstTrack;
		gVars->palmVolume = gameInfoP->musicInfo.sound.music ? gameInfoP->musicInfo.volume.palm : 0;

		MemHandleUnlock(recordH);
	} // end no game / game selected

	// common command line options

	// debug level
	if (gPrefs->debug) {
		StrIToA(num, gPrefs->debugLevel);
		ArgsAdd(&argvP[argc], "-d", num, &argc);
	}

	if (engine == ENGINE_QUEEN || engine == ENGINE_SKY) {
		// alternative intro ?
		if (gPrefs->altIntro)
			ArgsAdd(&argvP[argc], "--alt-intro", NULL, &argc);
	}
		
	if (engine == ENGINE_SCUMM) {
		// demo mode ?
		if (gPrefs->demoMode)
			ArgsAdd(&argvP[argc], "--demo-mode", NULL, &argc);
	}

	// copy protection ?
	if (gPrefs->copyProtection)
		ArgsAdd(&argvP[argc], "--copy-protection", NULL, &argc);

	// exceed max args ?
	if (argc > MAX_ARG)
		FrmCustomAlert(FrmErrorAlert, "Too many parameters.",0,0);

	// set some common options
	stackSize = (gPrefs->setStack ? STACK_LARGER : STACK_DEFAULT);
	lightspeed= (gPrefs->lightspeed.enable ? gPrefs->lightspeed.mode : 255);
	toLauncher= (gPrefs->exitLauncher);

	// gVars values 
	// (gVars->HRrefNum defined in checkHRmode on Cli�)
	gVars->VFS.volRefNum	= gPrefs->card.volRefNum;
	gVars->vibrator			= gPrefs->vibrator;
	gVars->stdPalette		= gPrefs->stdPalette;
	gVars->VFS.cacheSize	= (gPrefs->card.useCache ? gPrefs->card.cacheSize : 0);
	gVars->indicator.showLED= gPrefs->card.showLED;
	gVars->stylusClick		= gPrefs->stylusClick;
	gVars->autoSave			= (gPrefs->autoSave ? gPrefs->autoSavePeriod : -1);
	gVars->advancedMode		= gPrefs->advancedMode;

	// user params
	HWR_RSTALL();
	
	if (gPrefs->goLCD)
		HWR_SET(INIT_GOLCD);
	else
		OPTIONS_RST(kOptGoLcdAPI);
	
	if (!gPrefs->autoOff)
		HWR_SET(INIT_AUTOOFF);

	if (gVars->vibrator)
		HWR_SET(INIT_VIBRATOR);
/* ????
	if (	musicDriver == 1 ||
			musicDriver == 3 ||
			musicDriver == 4 ||
			musicDriver == sysInvalidRefNum) {
		HWR_SET(INIT_PA1LIB);
	}
*/
	if (ModImport(gVars->VFS.volRefNum, engine, &isARM) != errNone) {
		if (bDirectMode) {
			// and force exit if nothing selected
			EventType event;
			event.eType = keyDownEvent;
			event.data.keyDown.chr = vchrLaunch;
			event.data.keyDown.modifiers = commandKeyMask;
			EvtAddUniqueEventToQueue(&event, 0, true);
		}
		ArgsFree(argvP);
		return false;
	}

	// reset mode if screen rotation occured (DIA only)
	if (!direct && OPTIONS_TST(kOptCollapsible)) {
		UInt8 mode = PalmScreenSize(0,0, &(gVars->screenFullWidth), &(gVars->screenFullHeight));
		OPTIONS_RST(kOptModeLandscape);
		OPTIONS_SET((mode == PALM_LANDSCAPE) ? kOptModeLandscape : kOptNone);
	}

	// free and save globals pref memory
	GamCloseDatabase(false);
	FrmCloseAllForms();
	SavePrefs();

	{
		UInt16 cardNo;
		UInt32 dbID;

		LaunchParamType *cmdPBP = (LaunchParamType *)MemPtrNew(sizeof(LaunchParamType));

		MemPtrSetOwner(cmdPBP, 0);
		MemPtrSetOwner(gVars, 0);
		ArgsSetOwner(argvP, 0);

		cardNo = 0;
		dbID = DmFindDatabase(0, "ScummVM-Engine");

		if (isARM)
			FtrSet(appFileCreator, ftrStack , (stackSize * 4));
		else
			ModSetStack(stackSize, cardNo, dbID);

		cmdPBP->args.argc = argc;
		cmdPBP->args.argv = argvP;
		cmdPBP->gVars = gVars;
		cmdPBP->lightspeed = lightspeed;
		cmdPBP->exitLauncher = toLauncher;

		SysUIAppSwitch(cardNo, dbID, sysAppLaunchCmdNormalLaunch, cmdPBP);
		bLaunched = true;
	}

	return false;
}