/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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 <VFSMgr.h>
#include <ctype.h>

#include "globals.h"
#include "palmdefs.h"
#include "start.h"
#include "games.h"
#include "skin.h"

#include "StarterRsc.h"

DmOpenRef gameDB = NULL;

static Err GamUpdateList() {
	if (gameDB) {
		UInt16 numRecs = DmNumRecords(gameDB);

		if (numRecs > 0) {
			MemHandle tmpH;
			UInt32 version, size;
			UInt32 *versionP;

			// get record size and version
			tmpH = DmQueryRecord(gameDB, 0);
			size = MemHandleSize(tmpH);
			versionP = (UInt32 *)MemHandleLock(tmpH);
			version = *versionP;
			MemHandleUnlock(tmpH);

			// check record
			if (version != curItemVersion || size != sizeof(GameInfoType)) {
				UInt16 index;
				GameInfoType gitCur;
				void *tmpP;
				FormPtr ofmP, frmP;

				// show dialog
				ofmP = FrmGetActiveForm();
				frmP = FrmInitForm(ConvertForm);
				FrmSetActiveForm(frmP);
				FrmDrawForm(frmP);
				SysTaskDelay(1 * SysTicksPerSecond());

				MemSet(&gitCur, sizeof(GameInfoType), 0);

				if (version == itemVersion_357 ||
					version == itemVersion_356 ||
					version == itemVersion_355 ||
					version == itemVersion_354 ||
					version == itemVersion_353 ||
					version == itemVersion_352 ||
					version == itemVersion_351 ||
					version == itemVersion_350 ||
					version == itemVersion_340 ||
					version == itemVersion_330 ||
					version == itemVersion_320 ||
					version == itemVersion_310 ||
					version == itemVersion_300 ||
					version == itemVersion_270 ||
					version == itemVersion_260 ||
					version == itemVersion_250) {
					for (index = 0; index < numRecs; index++) {
						// get old data
						tmpH = DmQueryRecord(gameDB, index);
						tmpP = MemHandleLock(tmpH);
						MemMove(&gitCur, tmpP, MemHandleSize(tmpH));
						MemHandleUnlock(tmpH);

						// new format
						gitCur.version = curItemVersion;

						if (version < itemVersion_300) {
							gitCur.musicInfo.volume.palm = 50;
							gitCur.musicInfo.volume.music = 192;
							gitCur.musicInfo.volume.sfx = 192;
							gitCur.musicInfo.volume.speech = 192;
							gitCur.musicInfo.volume.audiocd = 50;

							gitCur.musicInfo.sound.tempo = 100;
							gitCur.musicInfo.sound.defaultTrackLength = 10;
							gitCur.musicInfo.sound.firstTrack = 1;
						}

						if (version < itemVersion_310)
							gitCur.engine = 0;

						if (version < itemVersion_320)
							gitCur.renderMode = 0;

						if (version <= itemVersion_330) {
							gitCur.fmQuality = 0;
							gitCur.gfxMode = (gitCur.gfxMode == 3 ? 1 : 0); // v3.4 only 2 modes

									if (gitCur.engine == 0) gitCur.engine = 8;
							else	if (gitCur.engine == 1) gitCur.engine = 7;
							else	if (gitCur.engine == 2) gitCur.engine = 3;
							else	if (gitCur.engine == 3) gitCur.engine = 1;
							else	if (gitCur.engine == 4) gitCur.engine = 0;
							else	if (gitCur.engine == 5) gitCur.engine = 6;
							else	if (gitCur.engine == 6) gitCur.engine = 4;
							else	if (gitCur.engine == 7) gitCur.engine = 5;
							else	if (gitCur.engine == 8) gitCur.engine = 2;

									if (gitCur.renderMode == 1) gitCur.renderMode = 4;
							else	if (gitCur.renderMode == 2) gitCur.renderMode = 5;
							else	if (gitCur.renderMode == 3) gitCur.renderMode = 2;
							else	if (gitCur.renderMode == 4) gitCur.renderMode = 3;
							else	if (gitCur.renderMode == 5) gitCur.renderMode = 1;

									if (gitCur.platform == 2) gitCur.platform = 6;
							else	if (gitCur.platform == 5) gitCur.platform = 8;
							else	if (gitCur.platform == 6) gitCur.platform = 2;

									if (gitCur.language ==  1) gitCur.language = 4;
							else	if (gitCur.language ==  2) gitCur.language = 6;
							else	if (gitCur.language ==  3) gitCur.language = 5;
							else	if (gitCur.language ==  4) gitCur.language = 8;
							else	if (gitCur.language ==  5) gitCur.language = 11;
							else	if (gitCur.language ==  6) gitCur.language = 13;
							else	if (gitCur.language ==  7) gitCur.language = 9;
							else	if (gitCur.language ==  8) gitCur.language = 1;
							else	if (gitCur.language ==  9) gitCur.language = 10;
							else	if (gitCur.language == 10) gitCur.language = 7;
							else	if (gitCur.language == 11) gitCur.language = 12;
							else	if (gitCur.language == 12) gitCur.language = 2;
							else	if (gitCur.language == 13) gitCur.language = 3;

									if (gitCur.musicInfo.sound.drvMusic == 1) gitCur.musicInfo.sound.drvMusic = 4;
							else	if (gitCur.musicInfo.sound.drvMusic == 2) gitCur.musicInfo.sound.drvMusic = 5;
							else	if (gitCur.musicInfo.sound.drvMusic == 4) gitCur.musicInfo.sound.drvMusic = 2;
							else	if (gitCur.musicInfo.sound.drvMusic == 5) gitCur.musicInfo.sound.drvMusic = 1;
						}

						if (version <= itemVersion_340) {
							gitCur.platform++;

							if (gitCur.language == 3)
								gitCur.language = 0;
							else if (gitCur.language >= 11)
								gitCur.language++;
						}

						if (version <= itemVersion_350)
							if (gitCur.platform >= 9)
								gitCur.platform++;

						if (version <= itemVersion_351) {
							if (gitCur.engine >= ENGINE_LURE)	// newly added engine
								gitCur.engine++;

							if (gitCur.engine == ENGINE_SCUMM)	// reorder
								gitCur.engine = ENGINE_AGOS;
							else if (gitCur.engine == ENGINE_AGOS)
								gitCur.engine = ENGINE_SCUMM;
						}

						if (version <= itemVersion_352) {
							if (gitCur.engine >= ENGINE_CINE)	// newly added engine
								gitCur.engine++;

							gitCur.platform++;
						}

						if (version <= itemVersion_353)
							gitCur.musicInfo.sound.drvMusic++;

						if (version <= itemVersion_354)
							if (gitCur.engine >= ENGINE_AGI)	// newly added engine
								gitCur.engine++;

						if (version <= itemVersion_355) {
								gitCur.engine++;				// renamed Simon -> AGOS
								if (gitCur.engine == ENGINE_COUNT)
									gitCur.engine = ENGINE_AGOS;
						}

						if (version <= itemVersion_356) {
							if (gitCur.platform >= 3)
								gitCur.platform++;
						}

						if (version <= itemVersion_357)
							if (gitCur.engine >= ENGINE_PARALLACTION)	// newly added engine
								gitCur.engine++;

						if (gitCur.musicInfo.volume.palm > 100)
							gitCur.musicInfo.volume.palm = 50;

						// simply resize the old record
						tmpH = DmResizeRecord(gameDB, index, sizeof(GameInfoType));	// TODO : check error on resize tmpH==NULL
						tmpP = MemHandleLock(tmpH);
						DmWrite(tmpP, 0, &gitCur, sizeof(GameInfoType));
						MemPtrUnlock(tmpP);
					}

				} else if (version == itemVersion_200) {
					// need conversion from V2 -> V3.5.2
					GameInfoTypeV2 git0;

					for (index = 0; index < numRecs; index++) {

						// get old data
						tmpH = DmQueryRecord(gameDB, index);
						tmpP = MemHandleLock(tmpH);
						MemMove(&git0, tmpP, sizeof(GameInfoTypeV2));
						MemHandleUnlock(tmpH);

						// convert to new format
						gitCur.version = curItemVersion;
						gitCur.icnID = 0xFFFF;
						gitCur.selected = git0.selected;
						StrCopy(gitCur.nameP, git0.nameP);
						StrCopy(gitCur.pathP, git0.pathP);
						StrCopy(gitCur.gameP, git0.gameP);
						gitCur.gfxMode = (git0.gfxMode == 3 ? 1 : 0); // v3.4 only 2 modes

						gitCur.autoLoad = git0.autoLoad;
						gitCur.bootParam = git0.bootParam;
						gitCur.setPlatform = git0.setPlatform;
						gitCur.subtitles = git0.subtitles;
						gitCur.talkSpeed = git0.talkSpeed;

						gitCur.loadSlot = git0.loadSlot;
						gitCur.bootValue = git0.bootValue;
						gitCur.talkValue = git0.talkValue;
						gitCur.platform = git0.platform;
						gitCur.language = git0.language;

						gitCur.musicInfo.volume.palm = 50;
						gitCur.musicInfo.volume.music = 192;
						gitCur.musicInfo.volume.sfx = 192;
						gitCur.musicInfo.volume.speech = 192;
						gitCur.musicInfo.volume.audiocd = 50;

						gitCur.musicInfo.sound.tempo = 100;
						gitCur.musicInfo.sound.defaultTrackLength = 10;
						gitCur.musicInfo.sound.firstTrack = 1;

						// to V3.4
								if (gitCur.platform == 2) gitCur.platform = 6;
						else	if (gitCur.platform == 5) gitCur.platform = 8;
						else	if (gitCur.platform == 6) gitCur.platform = 2;

								if (gitCur.language ==  1) gitCur.language = 4;
						else	if (gitCur.language ==  2) gitCur.language = 6;
						else	if (gitCur.language ==  3) gitCur.language = 5;
						else	if (gitCur.language ==  4) gitCur.language = 8;
						else	if (gitCur.language ==  5) gitCur.language = 11;
						else	if (gitCur.language ==  6) gitCur.language = 13;
						else	if (gitCur.language ==  7) gitCur.language = 9;
						else	if (gitCur.language ==  8) gitCur.language = 1;
						else	if (gitCur.language ==  9) gitCur.language = 10;
						else	if (gitCur.language == 10) gitCur.language = 7;
						else	if (gitCur.language == 11) gitCur.language = 12;
						else	if (gitCur.language == 12) gitCur.language = 2;
						else	if (gitCur.language == 13) gitCur.language = 3;

								if (gitCur.musicInfo.sound.drvMusic == 1) gitCur.musicInfo.sound.drvMusic = 4;
						else	if (gitCur.musicInfo.sound.drvMusic == 2) gitCur.musicInfo.sound.drvMusic = 5;
						else	if (gitCur.musicInfo.sound.drvMusic == 4) gitCur.musicInfo.sound.drvMusic = 2;
						else	if (gitCur.musicInfo.sound.drvMusic == 5) gitCur.musicInfo.sound.drvMusic = 1;

						// to V3.5
						gitCur.platform++;

						if (gitCur.language == 3)
							gitCur.language = 0;
						else if (gitCur.language >= 11)
							gitCur.language++;

						// to V3.5.1
						if (gitCur.platform >= 9)
							gitCur.platform++;

						//to V3.5.3
						gitCur.musicInfo.sound.drvMusic++;

						// -----
						gitCur.engine = ENGINE_SCUMM;

						tmpH = DmResizeRecord(gameDB, index, sizeof(GameInfoType));	// TODO : check error on resize tmpH==NULL
						tmpP = MemHandleLock(tmpH);
						DmWrite(tmpP, 0, &gitCur, sizeof(GameInfoType));
						MemPtrUnlock(tmpP);
					}
				} else {
					// need conversion from V0 -> V3.5.2
					GameInfoTypeV0 git0;

					for (index = 0; index < numRecs; index++) {

						// get old data
						tmpH = DmQueryRecord(gameDB, index);
						tmpP = MemHandleLock(tmpH);
						MemMove(&git0, tmpP, sizeof(GameInfoTypeV0));
						MemHandleUnlock(tmpH);

						// convert to new format
						gitCur.version = curItemVersion;
						gitCur.icnID = 0xFFFF;
						gitCur.selected = git0.selected;
						StrCopy(gitCur.nameP, git0.nameP);
						StrCopy(gitCur.pathP, git0.pathP);
						StrCopy(gitCur.gameP, git0.gameP);
						gitCur.gfxMode = (git0.gfxMode == 3 ? 1 : 0); // v3.4 only 2 modes

						gitCur.autoLoad = git0.autoLoad;
						gitCur.bootParam = git0.bootParam;
						gitCur.setPlatform = git0.amiga;	// amiga become platform amiga/atari-st/machintosh
						gitCur.platform = 1;
						gitCur.subtitles = git0.subtitles;
						gitCur.talkSpeed = git0.talkSpeed;

						gitCur.loadSlot = git0.loadSlot;
						gitCur.bootValue = git0.bootValue;
						gitCur.talkValue = git0.talkValue;
						gitCur.platform = 2;	// default to amiga
						gitCur.language = git0.language;

						gitCur.musicInfo.volume.palm = 50;
						gitCur.musicInfo.volume.music = 192;
						gitCur.musicInfo.volume.sfx = 192;
						gitCur.musicInfo.volume.speech = 192;
						gitCur.musicInfo.volume.audiocd = 50;

						gitCur.musicInfo.sound.tempo = 100;
						gitCur.musicInfo.sound.defaultTrackLength = 10;
						gitCur.musicInfo.sound.firstTrack = 1;

						gitCur.engine = ENGINE_SCUMM;

						tmpH = DmResizeRecord(gameDB, index, sizeof(GameInfoType));	// TODO : check error on resize tmpH==NULL
						tmpP = MemHandleLock(tmpH);
						DmWrite(tmpP, 0, &gitCur, sizeof(GameInfoType));
						MemPtrUnlock(tmpP);
					}
				}

				FrmEraseForm(frmP);
				FrmDeleteForm(frmP);
				if (ofmP)
					FrmSetActiveForm(ofmP);
			}
		}
	}

	return errNone;
}

Err GamOpenDatabase() {
	Err err = errNone;

	gameDB = DmOpenDatabaseByTypeCreator( 'DATA', appFileCreator, dmModeReadWrite);

	if (!gameDB) {
		err = DmCreateDatabase(0, "ScummVM-Data", appFileCreator, 'DATA', false);
		if (!err) {
			gameDB = DmOpenDatabaseByTypeCreator( 'DATA', appFileCreator, dmModeReadWrite);

			if (!gameDB)
				err = DmGetLastErr();
		}
	}

	if (err)
		FrmCustomAlert(FrmErrorAlert,"Cannot open/create games list DB !",0,0);
	else
		err = GamUpdateList();

	return err;
}

void GamImportDatabase() {
	if (gPrefs->card.volRefNum != vfsInvalidVolRef && gPrefs->card.moveDB) {
		FileRef file;
		Err e;

		e = VFSFileOpen(gPrefs->card.volRefNum, "/Palm/Programs/ScummVM/listdata.pdb", vfsModeRead, &file);
		if (!e) {
			UInt16 oCardNo, nCardNo;
			LocalID oDbID, nDbID;

			VFSFileClose(file);
			if (gPrefs->card.confirmMoveDB)
				if (FrmCustomAlert(FrmConfirmAlert, "Do you want to import games database from memory card ?", 0, 0) == FrmConfirmNo) {
					// prevent to replace the file on memory card
					gPrefs->card.moveDB = false;
					return;
				}

			// get current db info and rename it
			DmOpenDatabaseInfo(gameDB, &oDbID, 0, 0, &oCardNo, 0);
			GamCloseDatabase(true);
			e = DmDeleteDatabase(oCardNo, oDbID);

			if (!e)
				if (e = VFSImportDatabaseFromFile(gPrefs->card.volRefNum, "/Palm/Programs/ScummVM/listdata.pdb", &nCardNo, &nDbID))
					FrmCustomAlert(FrmErrorAlert, "Failed to import games database from memory card.", 0, 0);

			GamOpenDatabase();
		}
	}
}

void GamCloseDatabase(Boolean ignoreCardParams) {
	if (gameDB) {
		LocalID dbID;
		UInt16 cardNo;

		DmOpenDatabaseInfo(gameDB, &dbID, 0, 0, &cardNo, 0);
		DmCloseDatabase(gameDB);

		if (!ignoreCardParams) {
			if (gPrefs->card.moveDB && gPrefs->card.volRefNum != vfsInvalidVolRef) {
				VFSFileRename(gPrefs->card.volRefNum, "/Palm/Programs/ScummVM/listdata.pdb", "listdata-old.pdb");
				Err e = VFSExportDatabaseToFile(gPrefs->card.volRefNum, "/Palm/Programs/ScummVM/listdata.pdb", cardNo, dbID);
				if (!e) {
					VFSFileDelete(gPrefs->card.volRefNum, "/Palm/Programs/ScummVM/listdata-old.pdb");
					if (gPrefs->card.deleteDB)
						DmDeleteDatabase(cardNo, dbID);
				} else {
					VFSFileRename(gPrefs->card.volRefNum, "/Palm/Programs/ScummVM/listdata-old.pdb", "listdata.pdb");
				}
			}
		}
	}
	gameDB = NULL;
}

static Int16 GamCompare(GameInfoType *a, GameInfoType *b, SortRecordInfoPtr, SortRecordInfoPtr, MemHandle) {
	return StrCaselessCompare(a->nameP, b->nameP);
}

Err GamSortList() {
	return DmQuickSort (gameDB, (DmComparF *)GamCompare, 0);
}

void GamUnselect() {
	GameInfoType *game;

	MemHandle recordH;
	UInt16 index;

	index = GamGetSelected();

	if (index != dmMaxRecordIndex) {
		Boolean newValue;

		recordH = DmGetRecord(gameDB, index);
		game = (GameInfoType *)MemHandleLock(recordH);

		newValue = false;
		DmWrite(game, OffsetOf(GameInfoType,selected), &newValue, sizeof(Boolean));

		MemHandleUnlock(recordH);
		DmReleaseRecord (gameDB, index, 0);
	}
}

UInt16 GamGetSelected() {
	MemHandle record;
	GameInfoType *game;
	Boolean selected;
	UInt16 index = DmNumRecords(gameDB)-1;

	while (index != (UInt16)-1) {
		record = DmQueryRecord(gameDB, index);
		game = (GameInfoType *)MemHandleLock(record);
		selected = game->selected;
		MemHandleUnlock(record);

		if (selected)
			return index;

		index--;
	}

	return dmMaxRecordIndex;
}

Boolean GamJumpTo(Char letter) {
	MemHandle record;
	GameInfoType *game;
	Boolean found = false;
	UInt16 index = 0;
	UInt16 maxIndex = DmNumRecords(gameDB);
	UInt16 active = GamGetSelected();

	while (index < maxIndex) {
		record = DmGetRecord(gameDB, index);
		game = (GameInfoType *)MemHandleLock(record);

		if (tolower(game->nameP[0]) == tolower(letter)) {
			found = true;

			if (index != active) {
				RectangleType rArea;
				UInt16 maxView;
				Boolean newValue = true;

				SknGetListBounds(&rArea, NULL);
				maxView = rArea.extent.y / sknInfoListItemSize;

				GamUnselect();
				DmWrite(game, OffsetOf(GameInfoType,selected), &newValue, sizeof(Boolean));

				if (index < gPrefs->listPosition || index >= (gPrefs->listPosition + maxView))
					gPrefs->listPosition = index;
			}
		}

		MemHandleUnlock(record);
		DmReleaseRecord (gameDB, index, 0);

		index++;

		if (found)
			return found;
	}

	return found;
}