/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-10-0a/engines/cruise/overlay.cpp $
 * $Id: overlay.cpp 27024 2007-05-30 21:56:52Z fingolfin $
 *
 */

#include "cruise/cruise_main.h"

namespace Cruise {

overlayStruct overlayTable[90];
int numOfLoadedOverlay;

void initOverlayTable(void) {
	int i;

	for (i = 0; i < 90; i++) {
		overlayTable[i].overlayName[0] = 0;
		overlayTable[i].ovlData = NULL;
		overlayTable[i].alreadyLoaded = 0;
		overlayTable[i].executeScripts = 0;
	}

	numOfLoadedOverlay = 1;
}

int loadOverlay(const uint8 *scriptName) {
	int newNumberOfScript;
	bool scriptNotLoadedBefore;
	int scriptIdx;
	uint8 fileName[50];
	int fileIdx;
	int unpackedSize;
	char *unpackedBuffer;
	char *scriptPtr;
	ovlDataStruct *ovlData;

	printf("Load overlay: %s\n", scriptName);

	newNumberOfScript = numOfLoadedOverlay;

	scriptNotLoadedBefore = false;

	scriptIdx = findOverlayByName((const char *)scriptName);

	if (scriptIdx == -4) {
		scriptIdx = numOfLoadedOverlay;

		newNumberOfScript++;

		scriptNotLoadedBefore = true;
	}

	if (overlayTable[scriptIdx].alreadyLoaded) {
		return (scriptIdx);
	}

	overlayTable[scriptIdx].ovlData =
	    (ovlDataStruct *) mallocAndZero(sizeof(ovlDataStruct));

	if (!overlayTable[scriptIdx].ovlData)
		return (-2);

	strcpyuint8(overlayTable[scriptIdx].overlayName, scriptName);

	overlayTable[scriptIdx].alreadyLoaded = 1;

	numOfLoadedOverlay = newNumberOfScript;

	overlayTable[scriptIdx].ovlData->scriptNumber = scriptIdx;

	strcpyuint8(fileName, scriptName);

	strcatuint8(fileName, ".OVL");

	printf("Attempting to load overlay file %s...\n", fileName);

	fileIdx = findFileInDisks(fileName);

	if (fileIdx < 0) {
		printf("Unable to load overlay %s !\n", scriptName);
		//releaseScript(scriptName);
		return (-18);
	}

	unpackedSize = volumePtrToFileDescriptor[fileIdx].extSize + 2;

	// TODO: here, can unpack in gfx module buffer
	unpackedBuffer = (char *)mallocAndZero(unpackedSize);

	if (!unpackedBuffer) {
		return (-2);
	}

	if (volumePtrToFileDescriptor[fileIdx].size + 2 != unpackedSize) {
		char *pakedBuffer =
		    (char *)mallocAndZero(volumePtrToFileDescriptor[fileIdx].
		    size + 2);

		loadPakedFileToMem(fileIdx, (uint8 *) pakedBuffer);

		delphineUnpack((uint8 *)unpackedBuffer, (const uint8 *)pakedBuffer, volumePtrToFileDescriptor[fileIdx].size);

		free(pakedBuffer);
	} else {
		loadPakedFileToMem(fileIdx, (uint8 *) unpackedBuffer);
	}

	printf("OVL loading done...\n");

	scriptPtr = unpackedBuffer;

	ovlData = overlayTable[scriptIdx].ovlData;

	memcpy(ovlData, scriptPtr, sizeof(ovlDataStruct));

	ovlData->data3Table = NULL;
	ovlData->ptr1 = NULL;
	ovlData->objDataTable = NULL;
	ovlData->objData2SourceTable = NULL;
	ovlData->objData2WorkTable = NULL;
	ovlData->stringTable = NULL;
	ovlData->exportDataPtr = NULL;
	ovlData->importDataPtr = NULL;
	ovlData->linkDataPtr = NULL;
	ovlData->specialString1 = NULL;
	ovlData->specialString2 = NULL;
	ovlData->importNamePtr = NULL;
	ovlData->exportNamesPtr = NULL;
	ovlData->data4Ptr = NULL;
	ovlData->ptr8 = NULL;
	ovlData->numScripts1 = readB16(scriptPtr + 60);
	ovlData->numScripts2 = readB16(scriptPtr + 62);
	ovlData->numExport = readB16(scriptPtr + 64);
	ovlData->numImport = readB16(scriptPtr + 66);
	ovlData->numLinkData = readB16(scriptPtr + 68);
	ovlData->numObjData = readB16(scriptPtr + 70);
	ovlData->numStrings = readB16(scriptPtr + 72);
	ovlData->size8 = readB16(scriptPtr + 74);
	ovlData->size9 = readB16(scriptPtr + 76);
	ovlData->nameExportSize = readB16(scriptPtr + 78);
	ovlData->exportNamesSize = readB16(scriptPtr + 80);
	ovlData->specialString2Length = readB16(scriptPtr + 82);
	ovlData->sizeOfData4 = readB16(scriptPtr + 84);
	ovlData->size12 = readB16(scriptPtr + 86);
	ovlData->specialString1Length = readB16(scriptPtr + 88);
	ovlData->scriptNumber = readB16(scriptPtr + 90);

	scriptPtr += 92;

	if (ovlData->numExport)	{ // export data
		int i;
		ovlData->exportDataPtr =
		    (exportEntryStruct *) mallocAndZero(ovlData->numExport *
		    sizeof(exportEntryStruct));

		if (!ovlData->exportDataPtr) {
			return (-2);
		}

		for (i = 0; i < ovlData->numExport; i++) {
			ovlData->exportDataPtr[i].var0 = readB16(scriptPtr);
			ovlData->exportDataPtr[i].var2 =
			    readB16(scriptPtr + 2);
			ovlData->exportDataPtr[i].var4 =
			    readB16(scriptPtr + 4);
			ovlData->exportDataPtr[i].idx = readB16(scriptPtr + 6);
			ovlData->exportDataPtr[i].offsetToName =
			    readB16(scriptPtr + 8);

			scriptPtr += 10;
		}
	}

	if (ovlData->exportNamesSize) {	// export names
		ovlData->exportNamesPtr =
		    (uint8 *) mallocAndZero(ovlData->exportNamesSize);

		if (!ovlData->exportNamesPtr) {
			return (-2);
		}

		memcpy(ovlData->exportNamesPtr, scriptPtr,
		    ovlData->exportNamesSize);
		scriptPtr += ovlData->exportNamesSize;
	}

	if (ovlData->numImport) {	// import data
		int i;

		ovlData->importDataPtr =
		    (importDataStruct *) mallocAndZero(ovlData->numImport *
		    sizeof(importDataStruct));

		if (!ovlData->importDataPtr) {
			return (-2);
		}

		for (i = 0; i < ovlData->numImport; i++) {
			ovlData->importDataPtr[i].var0 = readB16(scriptPtr);
			ovlData->importDataPtr[i].var1 =
			    readB16(scriptPtr + 2);
			ovlData->importDataPtr[i].linkType =
			    readB16(scriptPtr + 4);
			ovlData->importDataPtr[i].linkIdx =
			    readB16(scriptPtr + 6);
			ovlData->importDataPtr[i].nameOffset =
			    readB16(scriptPtr + 8);

			scriptPtr += 10;
		}
	}

	if (ovlData->nameExportSize) {	// import name
		ovlData->importNamePtr =
		    (uint8 *) mallocAndZero(ovlData->nameExportSize);

		if (!ovlData->importNamePtr) {
			return (-2);
		}

		memcpy(ovlData->importNamePtr, scriptPtr,
		    ovlData->nameExportSize);
		scriptPtr += ovlData->nameExportSize;
	}

	if (ovlData->numLinkData) {	// link data
		ASSERT(sizeof(linkDataStruct) == 0x22);

		ovlData->linkDataPtr =
		    (linkDataStruct *) mallocAndZero(ovlData->numLinkData *
		    sizeof(linkDataStruct));

		if (!ovlData->linkDataPtr) {
			return (-2);
		}

		memcpy(ovlData->linkDataPtr, scriptPtr,
		    ovlData->numLinkData * sizeof(linkDataStruct));
		scriptPtr += ovlData->numLinkData * sizeof(linkDataStruct);
		flipGen(ovlData->linkDataPtr,
		    ovlData->numLinkData * sizeof(linkDataStruct));
	}

	if (ovlData->numScripts1) {	// script
		ovlData3Struct *tempPtr;
		int i;

		ovlData->data3Table =
		    (ovlData3Struct *) mallocAndZero(ovlData->numScripts1 *
		    sizeof(ovlData3Struct));

		if (!ovlData->data3Table) {
/*      releaseScript(scriptIdx,scriptName);

      if(freeIsNeeded) {
        freePtr(unpackedBuffer);
      } */

			return (-2);
		}

		memcpy(ovlData->data3Table, scriptPtr,
		    ovlData->numScripts1 * sizeof(ovlData3Struct));
		scriptPtr += ovlData->numScripts1 * 0x1C;

		flipGen(ovlData->data3Table,
		    ovlData->numScripts1 * sizeof(ovlData3Struct));

		tempPtr = ovlData->data3Table;

		for (i = 0; i < ovlData->numScripts1; i++) {
			uint8 *ptr = tempPtr->dataPtr =
			    (uint8 *) mallocAndZero(tempPtr->sizeOfData);

			if (!ptr) {
				/*      releaseScript(scriptIdx,scriptName);
				 * 
				 * if(freeIsNeeded)
				 * {
				 * freePtr(unpackedBuffer);
				 * } */

				return (-2);
			}

			memcpy(ptr, scriptPtr, tempPtr->sizeOfData);
			scriptPtr += tempPtr->sizeOfData;

			if (tempPtr->offsetToImportData) {
				flipGen(ptr + tempPtr->offsetToImportData,
				    tempPtr->numImport * 10);
			}

			if (tempPtr->offsetToSubData2) {
				flipGen(ptr + tempPtr->offsetToImportData,
				    tempPtr->subData2Size * 10);
			}

			tempPtr++;
		}
	}

	if (ovlData->numScripts2) {
		ovlData3Struct *tempPtr;
		int i;

		ovlData->ptr1 =
		    (uint8 *) mallocAndZero(ovlData->numScripts2 * 0x1C);

		if (!ovlData->ptr1) {
			return (-2);
		}

		memcpy(ovlData->ptr1, scriptPtr, ovlData->numScripts2 * 0x1C);
		scriptPtr += ovlData->numScripts2 * 0x1C;
		flipGen(ovlData->ptr1, ovlData->numScripts2 * 0x1C);

		tempPtr = (ovlData3Struct *) ovlData->ptr1;

		for (i = 0; i < ovlData->numScripts2; i++) {
			uint8 *ptr = tempPtr->dataPtr =
			    (uint8 *) mallocAndZero(tempPtr->sizeOfData);

			if (!ptr) {
				/*      releaseScript(scriptIdx,scriptName);
				 * 
				 * if(freeIsNeeded)
				 * {
				 * freePtr(unpackedBuffer);
				 * } */

				return (-2);
			}

			memcpy(ptr, scriptPtr, tempPtr->sizeOfData);
			scriptPtr += tempPtr->sizeOfData;

			if (tempPtr->offsetToImportData) {
				flipGen(ptr + tempPtr->offsetToImportData,
				    tempPtr->numImport * 10);
			}

			if (tempPtr->offsetToSubData2) {
				flipGen(ptr + tempPtr->offsetToImportData,
				    tempPtr->subData2Size * 10);
			}

			tempPtr++;
		}
	}

	if (ovlData->size12) {
		ovlData->ptr8 = (uint8 *) mallocAndZero(ovlData->size12);

		if (!ovlData->ptr8) {
/*      releaseScript(scriptIdx,scriptName);

      if(freeIsNeeded)
      {
        freePtr(unpackedBuffer);
      } */

			return (-2);
		}

		memcpy(ovlData->ptr8, scriptPtr, ovlData->size12);
		scriptPtr += ovlData->size12;
	}

	if (ovlData->numObjData) {
		int i;
		ovlData->objDataTable =
		    (objDataStruct *) mallocAndZero(ovlData->numObjData *
		    sizeof(objDataStruct));

		if (!ovlData->objDataTable) {
/*      releaseScript(scriptIdx,scriptName);

      if(freeIsNeeded)
      {
        freePtr(unpackedBuffer);
      } */

			return (-2);
		}

		for (i = 0; i < ovlData->numObjData; i++) {
			ovlData->objDataTable[i].var0 = *(int16 *) scriptPtr;
			scriptPtr += 2;
			flipShort(&ovlData->objDataTable[i].var0);

			ovlData->objDataTable[i].type = *(int16 *) scriptPtr;
			scriptPtr += 2;
			flipShort(&ovlData->objDataTable[i].type);

			ovlData->objDataTable[i].var2 = *(int16 *) scriptPtr;
			scriptPtr += 2;
			flipShort(&ovlData->objDataTable[i].var2);

			ovlData->objDataTable[i].var3 = *(int16 *) scriptPtr;
			scriptPtr += 2;
			flipShort(&ovlData->objDataTable[i].var3);

			ovlData->objDataTable[i].var4 = *(int16 *) scriptPtr;
			scriptPtr += 2;
			flipShort(&ovlData->objDataTable[i].var4);

			ovlData->objDataTable[i].var5 = *(int16 *) scriptPtr;
			scriptPtr += 2;
			flipShort(&ovlData->objDataTable[i].var5);

			ovlData->objDataTable[i].stateTableIdx = *(int16 *) scriptPtr;
			scriptPtr += 2;
			flipShort(&ovlData->objDataTable[i].stateTableIdx);
		}

		if (scriptNotLoadedBefore) {
			//int var1;
			//int var2;

			overlayTable[scriptIdx].state = (char)setup1;

			var1 = loadScriptSub1(scriptIdx, 3);
			var2 = loadScriptSub1(scriptIdx, 0);

			setup1 = var1 + var2;
		}
	}

	if (ovlData->size9) {
		ovlData->objData2WorkTable =
		    (objectParams *) mallocAndZero(ovlData->size9 *
		    sizeof(objectParams));
		memset(ovlData->objData2WorkTable, 0,
		    ovlData->size9 * sizeof(objectParams));

		if (!ovlData->objData2WorkTable) {
/*      releaseScript(scriptIdx,scriptName);

      if(freeIsNeeded)
      {
        freePtr(unpackedBuffer);
      } */

			return (-2);
		}
	}

	if (ovlData->size8) {
		ovlData->objData2SourceTable =
		    (objectParams *) mallocAndZero(ovlData->size8 *
		    sizeof(objectParams));

		if (!ovlData->objData2SourceTable) {
/*      releaseScript(scriptIdx,scriptName);

      if(freeIsNeeded)
      {
        freePtr(unpackedBuffer);
      } */

			return (-2);
		}

		memcpy(ovlData->objData2SourceTable, scriptPtr, ovlData->size8 * 12);	// TODO: made read item by item
		scriptPtr += ovlData->size8 * 12;
		flipGen(ovlData->objData2SourceTable, ovlData->size8 * 12);
	}

	if (ovlData->numStrings) {
		int i;

		ovlData->stringTable =
		    (stringEntryStruct *) mallocAndZero(ovlData->numStrings *
		    sizeof(stringEntryStruct));

		for (i = 0; i < ovlData->numStrings; i++) {
			ovlData->stringTable[i].idx = *(int16 *) scriptPtr;
			flipShort(&ovlData->stringTable[i].idx);
			scriptPtr += 2;
		}
	}

/*  if(freeIsNeeded)
  {
    freePtr(unpackedBuffer);
  } */

	if (ovlData->sizeOfData4) {
		ovlData->data4Ptr =
		    (uint8 *) mallocAndZero(ovlData->sizeOfData4);
		memset(ovlData->data4Ptr, 0, ovlData->sizeOfData4);

		if (!ovlData->data4Ptr) {
			//releaseScript(scriptIdx,scriptName);
			return (-2);
		}
	}

	if (ovlData->
	    specialString1Length /*|| ovlData->specialString2Length */ 
	    || ovlData->stringTable) {
		int i;
		//int unpackedSize;
		//int fileIdx;
		//uint8 fileName[50];
		//char* unpackedBuffer;

		strcpyuint8(fileName, scriptName);

		strcatuint8(fileName, ".FR");

		fileIdx = findFileInDisks(fileName);

		if (fileIdx < 0) {
			//releaseScript(scriptName);
			return (-18);
		}

		unpackedSize = volumePtrToFileDescriptor[fileIdx].extSize + 2;

		// TODO: here, can unpack in gfx module buffer
		unpackedBuffer = (char *)mallocAndZero(unpackedSize);

		if (!unpackedBuffer) {
			return (-2);
		}

		if (volumePtrToFileDescriptor[fileIdx].size + 2 !=
		    unpackedSize) {
			char *pakedBuffer =
			    (char *)
			    mallocAndZero(volumePtrToFileDescriptor[fileIdx].
			    size + 2);

			loadPakedFileToMem(fileIdx, (uint8 *) pakedBuffer);

			delphineUnpack((uint8 *) unpackedBuffer, (const uint8 *)pakedBuffer, volumePtrToFileDescriptor[fileIdx].size);

			free(pakedBuffer);
		} else {
			loadPakedFileToMem(fileIdx, (uint8 *) unpackedBuffer);
		}

		scriptPtr = unpackedBuffer;

		memcpy(&ovlData->specialString1Length, scriptPtr, 2);
		scriptPtr += 2;
		flipShort(&ovlData->specialString1Length);	// recheck if needed

		if (ovlData->specialString1Length) {
			ovlData->specialString1 =
			    (uint8 *) mallocAndZero(ovlData->
			    specialString1Length);

			if (!ovlData->specialString1) {
				/*      releaseScript(scriptIdx,scriptName);
				 * 
				 * if(freeIsNeeded)
				 * {
				 * freePtr(unpackedBuffer);
				 * } */

				return (-2);
			}

			memcpy(ovlData->specialString1, scriptPtr,
			    ovlData->specialString1Length);
			scriptPtr += ovlData->specialString1Length;
		}

		memcpy(&ovlData->specialString2Length, scriptPtr, 2);
		scriptPtr += 2;
		flipShort(&ovlData->specialString2Length);	// recheck if needed

		if (ovlData->specialString2Length) {
			ovlData->specialString2 =
			    (uint8 *) mallocAndZero(ovlData->
			    specialString2Length);

			if (!ovlData->specialString2) {
				/*      releaseScript(scriptIdx,scriptName);
				 * 
				 * if(freeIsNeeded)
				 * {
				 * freePtr(unpackedBuffer);
				 * } */

				return (-2);
			}

			memcpy(ovlData->specialString2, scriptPtr,
			    ovlData->specialString2Length);
			scriptPtr += ovlData->specialString2Length;
		}

		for (i = 0; i < ovlData->numStrings; i++) {
			ovlData->stringTable[i].length = *(int16 *) scriptPtr;
			scriptPtr += 2;
			flipShort(&ovlData->stringTable[i].length);

			if (ovlData->stringTable[i].length) {
				ovlData->stringTable[i].string =
				    (char *)mallocAndZero(ovlData->
				    stringTable[i].length);

				if (!ovlData->stringTable[i].string) {
					/*      releaseScript(scriptIdx,scriptName);
					 * 
					 * if(freeIsNeeded)
					 * {
					 * freePtr(unpackedBuffer);
					 * } */

					return (-2);
				}

				memcpy(ovlData->stringTable[i].string,
				    scriptPtr, ovlData->stringTable[i].length);
				scriptPtr += ovlData->stringTable[i].length;
			}
		}
	}
#ifdef DUMP_SCRIPT
	{
		int i;
		for (i = 0; i < ovlData->numScripts1; i++) {
			dumpScript(scriptName, ovlData, i);
		}
	}
#endif
#ifdef DUMP_OBJECT
	{
		int i;
		FILE *fHandle;
		char nameBundle[100];
		sprintf(nameBundle, "%s-objs.txt", scriptName);

		fHandle = fopen(nameBundle, "w+");
		ASSERT(fHandle);

		for (i = 0; i < ovlData->numLinkData; i++) {
			linkDataStruct *var_34;
			var_34 = &ovlData->linkDataPtr[i];

			if (ovlData->specialString2) {
				fprintf(fHandle, "----- object %02d -----\n",
				    i);
				if (var_34->stringNameOffset != 0xFFFF) {
					fprintf(fHandle, "name: %s\n",
					    getObjectName(var_34->
						stringNameOffset,
						ovlData->specialString2));
				}
			}
		}

		fclose(fHandle);
	}
#endif

	return (scriptIdx);
}

int releaseOverlay(const char *name) {
	int overlayIdx;
	ovlDataStruct *ovlDataPtr;

	overlayIdx = findOverlayByName(name);

	if (overlayIdx == -4)
		return -4;

	if (overlayTable[overlayIdx].alreadyLoaded == 0)
		return -4;

	overlayTable[overlayIdx].alreadyLoaded = 0;

	ovlDataPtr = overlayTable[overlayIdx].ovlData;

	if (!ovlDataPtr)
		return -4;
/*
  if(overlayTable[overlayIdx].var1E)
  {
    free(overlayTable[overlayIdx].var1E);
    overlayTable[overlayIdx].var1E = NULL;
  }

  if(overlayTable[overlayIdx].var16)
  {
    free(overlayTable[overlayIdx].var16);
    overlayTable[overlayIdx].var16 = NULL;
  } */

	removeScript(overlayIdx, -1, &procHead);
	removeScript(overlayIdx, -1, &procHead);

	removeScript(overlayIdx, -1, &relHead);
	removeScript(overlayIdx, -1, &relHead);

	printf("releaseOverlay: finish !\n");

	return 0;
}

int32 findOverlayByName2(const uint8 *name) {
	int i;

	for (i = 1; i < numOfLoadedOverlay; i++) {
		if (!strcmpuint8(overlayTable[i].overlayName, name))
			return (i);
	}

	return (-4);
}

int findOverlayByName(const char *overlayName) {
	int i;

	for (i = 1; i < numOfLoadedOverlay; i++) {
		if (!strcmp(overlayTable[i].overlayName, overlayName)) {
			return (i);
		}
	}

	return (-4);
}

} // End of namespace Cruise
