/* 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */

#include "common/endian.h"
#include "cruise/cruise_main.h"

namespace Cruise {

exportEntryStruct *parseExport(int *out1, int *pExportedFuncionIdx, char *buffer) {
	char localBuffer[256];
	char functionName[256];
	char overlayName[256];
	char *dotPtr;
	char *ptr2;
	int idx;
	int numSymbGlob;
	exportEntryStruct *currentExportEntry;
	char *entity1Name;

	*out1 = 0;
	*pExportedFuncionIdx = 0;

	Common::strlcpy(localBuffer, buffer, sizeof(localBuffer));
	dotPtr = strchr(localBuffer, '.');

	if (dotPtr) {
		Common::strlcpy(functionName, dotPtr + 1, sizeof(functionName));
		*dotPtr = 0;

		strcpy(overlayName, localBuffer);
	} else {
		overlayName[0] = 0;

		Common::strlcpy(functionName, buffer, sizeof(functionName));
	}

	ptr2 = strchr((char *)functionName, ':');

	if (ptr2) {
		*ptr2 = 0;

		*out1 = 1;
	}

	strToUpper(overlayName);
	strToUpper(functionName);
	if (strlen((char *)overlayName) == 0)
		return nullptr;

	idx = findOverlayByName2(overlayName);

	if (idx == -4)
		return (nullptr);

	if (overlayTable[idx].alreadyLoaded == 0)
		return (nullptr);

	if (!overlayTable[idx].ovlData)
		return (nullptr);

	numSymbGlob = overlayTable[idx].ovlData->numSymbGlob;
	currentExportEntry = overlayTable[idx].ovlData->arraySymbGlob;
	entity1Name = overlayTable[idx].ovlData->arrayNameSymbGlob;

	if (!entity1Name)
		return (nullptr);

	for (int i = 0; i < numSymbGlob; i++) {
		char exportedName[256];
		char *name = entity1Name + currentExportEntry->offsetToName;

		Common::strlcpy(exportedName, name, sizeof(exportedName));
		strToUpper(exportedName);

		if (!strcmp(functionName, exportedName)) {
			*pExportedFuncionIdx = idx;

			return (currentExportEntry);
		}

		currentExportEntry++;
	}

	return (nullptr);
}

int updateScriptImport(int ovlIdx) {
	char buffer[256];
	ovlDataStruct *ovlData;
	int numData3;
	int size5;
	int numRelocGlob;
	int param;
	int var_32;
	ovlData3Struct *pScript;
//  char* arrayRelocGlob;
//  char* namePtr;
//  char* arrayMsgRelHeader;

	if (!overlayTable[ovlIdx].ovlData)
		return (-4);

	ovlData = overlayTable[ovlIdx].ovlData;

	numData3 = ovlData->numProc;
	size5 = ovlData->numRel;
	numRelocGlob = ovlData->numRelocGlob;
	param = 0;

	// do it for the 2 first string types
	do {

		if (param == 0) {
			var_32 = numData3;
		} else {
			var_32 = size5;
		}

		if (var_32) {
			int i = 0;
			do {
				importScriptStruct *ptrImportData;
				const char *ptrImportName;
				uint8 *ptrData;

				if (param == 0)
					pScript = getOvlData3Entry(ovlIdx, i);
				else
					pScript = scriptFunc1Sub2(ovlIdx, i);

				ptrImportData = (importScriptStruct *)(pScript->dataPtr + pScript->offsetToImportData);	// import data
				ptrImportName = (const char*)(pScript->dataPtr + pScript->offsetToImportName);	// import name
				ptrData = pScript->dataPtr;

				if (pScript->numRelocGlob > 0) {
					int counter = pScript->numRelocGlob;

					do {
						int param2 = ptrImportData->type;

						if (param2 != 70) {
							exportEntryStruct * ptrDest2;
							int out1;
							int out2;

							Common::strlcpy(buffer, ptrImportName + ptrImportData->offsetToName, sizeof(buffer));
							ptrDest2 = parseExport(&out1, &out2, buffer);

							if (ptrDest2 && out2) {
								int temp =
								    ptrImportData->
								    offset;
								if (out1) {	//is sub function... (ie  'invent.livre:s')
									uint8 *ptr = ptrData + temp;

									*(ptr + 1) = out2;
									WRITE_BE_UINT16(ptr + 2, ptrDest2->idx);
								} else {
									if (param2 == 20 || param2 == 30 || param2 == 40 || param2 == 50) {	// this patch a double push
										uint8 *ptr = ptrData + temp;

										*(ptr + 1) = 0;
										*(ptr + 2) = out2;	// update the overlay number

										WRITE_BE_UINT16(ptr + 4, ptrDest2->idx);
									} else {
										int var_4 = ptrDest2->var4;

										if (var_4 & 1) {
											param2 = 8;
										} else {
											param2 = 16;
										}

										if (var_4 >= 0 && var_4 <= 3) {
											param2 |= 5;
										} else {
											param2 |= 6;
										}

										*(ptrData + temp) = param2;
										*(ptrData + temp + 1) = out2;

										WRITE_BE_UINT16(ptrData + temp + 2, ptrDest2->idx);
									}
								}
							}
						}

						ptrImportData++;
					} while (--counter);
				}

			} while (++i < var_32);

		}

	} while (++param < 2);

	if (ovlData->arrayRelocGlob && ovlData->arrayNameRelocGlob && numRelocGlob) {
		int numImport2 = numRelocGlob;
		for (int i = 0; i < numImport2; i++) {
			int out1;
			int foundExportIdx;
			exportEntryStruct *pFoundExport;
			int linkType;
			int linkEntryIdx;

			Common::strlcpy(buffer, ovlData->arrayNameRelocGlob + ovlData->arrayRelocGlob[i].nameOffset, sizeof(buffer));

			pFoundExport = parseExport(&out1, &foundExportIdx, buffer);

			linkType = ovlData->arrayRelocGlob[i].linkType;
			linkEntryIdx = ovlData->arrayRelocGlob[i].linkIdx;

			if (pFoundExport && foundExportIdx) {
				switch (linkType) {
				case 0: {	// verb
					ovlData->arrayMsgRelHeader[linkEntryIdx].verbOverlay = foundExportIdx;
					ovlData->arrayMsgRelHeader[linkEntryIdx].verbNumber = pFoundExport->idx;
					break;
				}
				case 1: {	// obj1
					ovlData->arrayMsgRelHeader[linkEntryIdx].obj1Overlay = foundExportIdx;
					ovlData->arrayMsgRelHeader[linkEntryIdx].obj1Number = pFoundExport->idx;
					break;
				}
				case 2: {	// obj2
					ovlData->arrayMsgRelHeader[linkEntryIdx].obj2Overlay = foundExportIdx;
					ovlData->arrayMsgRelHeader[linkEntryIdx].obj2Number = pFoundExport->idx;
					break;
				}
				default:
					break;
				}
			}
		}
	}

	return (0);
}

// check that the newly loaded isn't used by the already loaded overlays
void updateAllScriptsImports() {
	for (int i = 0; i < 90; i++) {
		if (overlayTable[i].ovlData && overlayTable[i].alreadyLoaded)
			updateScriptImport(i);
	}
}

} // End of namespace Cruise
