/* 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.
 *
 */

/*
 * This code is based on Labyrinth of Time code with assistance of
 *
 * Copyright (c) 1993 Terra Nova Development
 * Copyright (c) 2004 The Wyrmkeep Entertainment Co.
 *
 */

#include "lab/lab.h"
#include "lab/resource.h"

namespace Lab {

Resource::Resource(LabEngine *vm) : _vm(vm) {
	readStaticText();
}

void Resource::readStaticText() {
	Common::File *labTextFile = openDataFile("Lab:Rooms/LabText");

	for (int i = 0; i < 48; i++)
		_staticText[i] = labTextFile->readLine();

	delete labTextFile;
}

TextFont *Resource::getFont(const char *fileName) {
	Common::File *dataFile = openDataFile(fileName, MKTAG('V', 'G', 'A', 'F'));

	uint32 headerSize = 4L + 2L + 256 * 3 + 4L;
	uint32 fileSize = dataFile->size();
	if (fileSize <= headerSize)
		return NULL;

	_vm->_music->updateMusic();

	TextFont *textfont = new TextFont();
	textfont->_dataLength = fileSize - headerSize;
	textfont->_height = dataFile->readUint16LE();
	dataFile->read(textfont->_widths, 256);
	for (int i = 0; i < 256; i++)
		textfont->_offsets[i] = dataFile->readUint16LE();
	dataFile->skip(4);
	textfont->_data = new byte[textfont->_dataLength + 4];
	dataFile->read(textfont->_data, textfont->_dataLength);
	return textfont;
}

char *Resource::getText(const char *fileName) {
	Common::File *dataFile = openDataFile(fileName);

	g_lab->_music->updateMusic();

	uint32 count = dataFile->size();
	byte *buffer = new byte[count];
	byte *text = buffer;
	dataFile->read(buffer, count);

	while (text && *text != '\0')
		*text++ -= (byte)95;

	delete dataFile;
	return (char *)buffer;
}

bool Resource::readRoomData(const char *fileName) {
	Common::File *dataFile = openDataFile(fileName, MKTAG('D', 'O', 'R', '1'));

	_vm->_manyRooms = dataFile->readUint16LE();
	_vm->_highestCondition = dataFile->readUint16LE();
	_vm->_rooms = new RoomData[_vm->_manyRooms + 1];
	memset(_vm->_rooms, 0, (_vm->_manyRooms + 1) * sizeof(RoomData));

	for (uint16 i = 1; i <= _vm->_manyRooms; i++) {
		_vm->_rooms[i]._northDoor = dataFile->readUint16LE();
		_vm->_rooms[i]._southDoor = dataFile->readUint16LE();
		_vm->_rooms[i]._eastDoor = dataFile->readUint16LE();
		_vm->_rooms[i]._westDoor = dataFile->readUint16LE();
		_vm->_rooms[i]._transitionType = dataFile->readByte();

		_vm->_rooms[i]._view[NORTH] = nullptr;
		_vm->_rooms[i]._view[SOUTH] = nullptr;
		_vm->_rooms[i]._view[EAST] = nullptr;
		_vm->_rooms[i]._view[WEST] = nullptr;
		_vm->_rooms[i]._rules = nullptr;
		_vm->_rooms[i]._roomMsg = nullptr;
	}

	delete dataFile;
	return true;
}

InventoryData *Resource::readInventory(const char *fileName) {
	Common::File *dataFile = openDataFile(fileName, MKTAG('I', 'N', 'V', '1'));

	_vm->_numInv = dataFile->readUint16LE();
	InventoryData *inventory = new InventoryData[_vm->_numInv + 1];

	for (uint16 i = 1; i <= _vm->_numInv; i++) {
		inventory[i]._many = dataFile->readUint16LE();
		inventory[i]._name = readString(dataFile);
		inventory[i]._bitmapName = readString(dataFile);
	}

	delete dataFile;

	return inventory;
}

bool Resource::readViews(uint16 roomNum) {
	Common::String fileName = "LAB:Rooms/" + Common::String::format("%d", roomNum);
	Common::File *dataFile = openDataFile(fileName.c_str(), MKTAG('R', 'O', 'M', '4'));

	_vm->_rooms[roomNum]._roomMsg = readString(dataFile);
	_vm->_rooms[roomNum]._view[NORTH] = readView(dataFile);
	_vm->_rooms[roomNum]._view[SOUTH] = readView(dataFile);
	_vm->_rooms[roomNum]._view[EAST] = readView(dataFile);
	_vm->_rooms[roomNum]._view[WEST] = readView(dataFile);
	_vm->_rooms[roomNum]._rules = readRule(dataFile);

	_vm->_music->updateMusic();

	delete dataFile;
	return true;
}

Common::String Resource::translateFileName(Common::String filename) {
	filename.toUppercase();
	Common::String fileNameStrFinal;

	if (filename.hasPrefix("P:")) {
		if (g_lab->_isHiRes)
			fileNameStrFinal = "GAME/SPICT/";
		else
			fileNameStrFinal = "GAME/PICT/";
	}
	else if (filename.hasPrefix("LAB:"))
		fileNameStrFinal = "GAME/";
	else if (filename.hasPrefix("MUSIC:"))
		fileNameStrFinal = "GAME/MUSIC/";

	if (filename.contains(':')) {
		while (filename[0] != ':') {
			filename.deleteChar(0);
		}

		filename.deleteChar(0);
	}

	fileNameStrFinal += filename;

	return fileNameStrFinal;
}

Common::File *Resource::openDataFile(const char *fileName, uint32 fileHeader) {
	Common::File *dataFile = new Common::File();
	dataFile->open(translateFileName(fileName));
	if (!dataFile->isOpen())
		error("openDataFile: Couldn't open %s (%s)", translateFileName(fileName).c_str(), fileName);

	if (fileHeader > 0) {
		uint32 headerTag = dataFile->readUint32BE();
		if (headerTag != fileHeader) {
			dataFile->close();
			error("openDataFile: Unexpected header in %s (%s) - expected: %d, got: %d", translateFileName(fileName).c_str(), fileName, fileHeader, headerTag);
		}
	}

	return dataFile;
}

char *Resource::readString(Common::File *file) {
	byte size = file->readByte();
	if (!size)
		return NULL;
	char *str = new char[size];
	char *c = str;
	for (int i = 0; i < size; i++) {
		*c = file->readByte();
		// Decrypt char
		*c = (i < size - 1) ? *c - 95 : '\0';
		c++;
	}

	return str;
}

int16 *Resource::readConditions(Common::File *file) {
	int16 i = 0, cond;
	int16 *list = new int16[25];
	memset(list, 0, 25 * 2);

	do {
		cond = file->readUint16LE();
		if (i < 25)
			list[i++] = cond;
	} while (cond);

	return list;
}

RuleList *Resource::readRule(Common::File *file) {
	char c;
	RuleList *rules = new RuleList();

	do {
		c = file->readByte();

		if (c == 1) {
			Rule *rule = new Rule();;
			rule->_ruleType = file->readSint16LE();
			rule->_param1 = file->readSint16LE();
			rule->_param2 = file->readSint16LE();
			rule->_condition = readConditions(file);
			rule->_actionList = readAction(file);
			rules->push_back(rule);
		}
	} while (c == 1);

	return rules;
}

Action *Resource::readAction(Common::File *file) {
	char c;
	Action *action = NULL;
	Action *prev = NULL;
	Action *head = NULL;
	char **messages;

	do {
		c = file->readByte();

		if (c == 1) {
			action = new Action();
			if (!head)
				head = action;
			if (prev)
				prev->_nextAction = action;
			action->_actionType = file->readSint16LE();
			action->_param1 = file->readSint16LE();
			action->_param2 = file->readSint16LE();
			action->_param3 = file->readSint16LE();

			if (action->_actionType == SHOWMESSAGES) {
				messages = (char **)malloc(action->_param1 * 4);

				for (int i = 0; i < action->_param1; i++)
					messages[i] = readString(file);

				action->_data = (byte *)messages;
			} else {
				action->_data = (byte *)readString(file);
			}

			action->_nextAction = NULL;
			prev = action;
		}
	} while (c == 1);

	return head;
}

CloseData *Resource::readCloseUps(uint16 depth, Common::File *file) {
	char c;
	CloseData *closeup = NULL;
	CloseData *prev = NULL;
	CloseData *head = NULL;

	do {
		c = file->readByte();

		if (c != '\0') {
			closeup = new CloseData();
			if (!head)
				head = closeup;
			if (prev)
				prev->_nextCloseUp = closeup;
			closeup->x1 = file->readUint16LE();
			closeup->y1 = file->readUint16LE();
			closeup->x2 = file->readUint16LE();
			closeup->y2 = file->readUint16LE();
			closeup->_closeUpType = file->readSint16LE();
			closeup->_depth = depth;
			closeup->_graphicName = readString(file);
			closeup->_message = readString(file);
			closeup->_subCloseUps = readCloseUps(depth + 1, file);
			closeup->_nextCloseUp = NULL;
			prev = closeup;
		}
	} while (c != '\0');

	return head;
}

ViewData *Resource::readView(Common::File *file) {
	char c;
	ViewData *view = NULL;
	ViewData *prev = NULL;
	ViewData *head = NULL;

	do {
		c = file->readByte();

		if (c == 1) {
			view = new ViewData();
			if (!head)
				head = view;
			if (prev)
				prev->_nextCondition = view;
			view->_condition = readConditions(file);
			view->_graphicName = readString(file);
			view->_closeUps = readCloseUps(0, file);
			view->_nextCondition = NULL;
			prev = view;
		}
	} while (c == 1);

	return head;
}

} // End of namespace Lab