/* 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 "mohawk/livingbooks.h"
#include "mohawk/resource.h"

#include "common/events.h"

namespace Mohawk {

MohawkEngine_LivingBooks::MohawkEngine_LivingBooks(OSystem *syst, const MohawkGameDescription *gamedesc) : MohawkEngine(syst, gamedesc) {
	_needsUpdate = false;
	_screenWidth = _screenHeight = 0;
}

MohawkEngine_LivingBooks::~MohawkEngine_LivingBooks() {
	delete _console;
	delete _gfx;
	_bookInfoFile.clear();
}

Common::Error MohawkEngine_LivingBooks::run() {
	MohawkEngine::run();

	_console = new LivingBooksConsole(this);
	_gfx = new LBGraphics(this);

	// Load the book info from the detected file
	loadBookInfo(getBookInfoFileName());

	if (!_title.empty()) // Some games don't have the title stored
		debug("Starting Living Books Title \'%s\'", _title.c_str());
	if (!_copyright.empty())
		debug("Copyright: %s", _copyright.c_str());

	if (!_screenWidth || !_screenHeight)
		error("Could not find xRes/yRes variables");

	debug("Setting screen size to %dx%d", _screenWidth, _screenHeight);

	// TODO: Eventually move this to a LivingBooksGraphics class or similar
	initGraphics(_screenWidth, _screenHeight, true, NULL);

	loadIntro();

	debug(1, "Stack Version: %d", getResourceVersion());

	_gfx->setPalette(1000);
	loadSHP(1000);
	loadANI(1000);

	// Code to Load Sounds For Debugging...
	//for (byte i = 0; i < 30; i++)
	//	_sound->playSound(1000+i);

	Common::Event event;
	while (!shouldQuit()) {
		while (_eventMan->pollEvent(event)) {
			switch (event.type) {
			case Common::EVENT_MOUSEMOVE:
				break;
			case Common::EVENT_LBUTTONUP:
				break;
			case Common::EVENT_LBUTTONDOWN:
				break;
			case Common::EVENT_KEYDOWN:
				switch (event.kbd.keycode) {
				case Common::KEYCODE_d:
					if (event.kbd.flags & Common::KBD_CTRL) {
						_console->attach();
						_console->onFrame();
					}
					break;
				case Common::KEYCODE_SPACE:
					pauseGame();
					break;
				default:
					break;
				}
				break;
			default:
				break;
			}
		}

		if (_needsUpdate) {
			_system->updateScreen();
			_needsUpdate = false;
		}

		// Cut down on CPU usage
		_system->delayMillis(10);
	}

	return Common::kNoError;
}

void MohawkEngine_LivingBooks::loadBookInfo(Common::String filename) {
	if (!_bookInfoFile.loadFromFile(filename))
		error("Could not open %s as a config file", filename.c_str());

	_title = getStringFromConfig("BookInfo", "title");
	_copyright = getStringFromConfig("BookInfo", "copyright");

	_numPages = getIntFromConfig("BookInfo", "nPages");
	_numLanguages = getIntFromConfig("BookInfo", "nLanguages");
	_screenWidth = getIntFromConfig("BookInfo", "xRes");
	_screenHeight = getIntFromConfig("BookInfo", "yRes");
	// nColors is here too, but it's always 256 anyway...

	// The later Living Books games add some more options:
	//     - fNeedPalette                (always true?)
	//     - fUse254ColorPalette         (always true?)
	//     - nKBRequired                 (4096, RAM requirement?)
	//     - fDebugWindow                (always 0?)
}

void MohawkEngine_LivingBooks::loadIntro() {
	Common::String filename;

	// We get to try for a few different names! Yay!
	filename = getFileNameFromConfig("Intro", "Page1");

	// Some store with .r, not sure why.
	if (filename.empty())
		filename = getFileNameFromConfig("Intro", "Page1.r");

	if (!filename.empty() && Common::File::exists(filename)) {
		MohawkArchive *introArchive = createMohawkArchive();
		introArchive->open(filename);
		_mhk.push_back(introArchive);
	}

	filename = getFileNameFromConfig("Intro", "Page2");

	if (filename.empty())
		filename = getFileNameFromConfig("Intro", "Page2.r");

	if (!filename.empty() && Common::File::exists(filename)) {
		MohawkArchive *coverArchive = createMohawkArchive();
		coverArchive->open(filename);
		_mhk.push_back(coverArchive);
	}
}

// Only 1 VSRN resource per stack, Id 1000
uint16 MohawkEngine_LivingBooks::getResourceVersion() {
	Common::SeekableReadStream *versionStream = getRawData(ID_VRSN, 1000);

	if (versionStream->size() != 2)
		warning("Version Record size mismatch");

	uint16 version = versionStream->readUint16BE();

	delete versionStream;
	return version;
}

// Multiple SHP# resource per stack.. Optional per Card?
// This record appears to be a list structure of BMAP resource Ids..
void MohawkEngine_LivingBooks::loadSHP(uint16 resourceId) {
	Common::SeekableSubReadStreamEndian *shpStream = wrapStreamEndian(ID_SHP, resourceId);

	if (shpStream->size() < 6)
		warning("SHP Record size too short");

	if (shpStream->readUint16() != 3)
		warning("SHP Record u0 not 3");

	if (shpStream->readUint16() != 0)
		warning("SHP Record u1 not 0");

	uint16 idCount = shpStream->readUint16();
	debug(1, "SHP: idCount: %d", idCount);

	if (shpStream->size() != (idCount * 2) + 6)
		warning("SHP Record size mismatch");

	uint16 *idValues = new uint16[idCount];
	for (uint16 i = 0; i < idCount; i++) {
		idValues[i] = shpStream->readUint16();
		debug(1, "SHP: BMAP Resource Id %d: %d", i, idValues[i]);
	}

	delete[] idValues;
	delete shpStream;
}

// Multiple ANI resource per stack.. Optional per Card?
void MohawkEngine_LivingBooks::loadANI(uint16 resourceId) {
	Common::SeekableSubReadStreamEndian *aniStream = wrapStreamEndian(ID_ANI, resourceId);

	if (aniStream->size() != 30)
		warning("ANI Record size mismatch");

	if (aniStream->readUint16() != 1)
		warning("ANI Record u0 not 0"); // Version?

	uint16 u1 = aniStream->readUint16();
	debug(1, "ANI u1: %d", u1);

	uint16 u2 = aniStream->readUint16();
	debug(1, "ANI u2: %d", u2);

	Common::Rect u3;
	u3.right = aniStream->readUint16();
	u3.bottom = aniStream->readUint16();
	u3.left = aniStream->readUint16();
	u3.top = aniStream->readUint16();
	debug(1, "ANI u3: (%d, %d), (%d, %d)", u3.left, u3.top, u3.right, u3.bottom);

	Common::Rect u4;
	u4.right = aniStream->readUint16();
	u4.bottom = aniStream->readUint16();
	u4.left = aniStream->readUint16();
	u4.top = aniStream->readUint16();
	debug(1, "ANI u4: (%d, %d), (%d, %d)", u4.left, u4.top, u4.right, u4.bottom);

	// BMAP Id?
	uint16 u4ResourceId = aniStream->readUint16();
	debug(1, "ANI u4ResourceId: %d", u4ResourceId);

	// Following 3 unknowns also resourceIds in Unused?
	uint16 u5 = aniStream->readUint16();
	debug(1, "ANI u5: %d", u5);
	if (u5 != 0)
		warning("ANI u5 non-zero");

	uint16 u6 = aniStream->readUint16();
	debug(1, "ANI u6: %d", u6);
	if (u6 != 0)
		warning("ANI u6 non-zero");

	uint16 u7 = aniStream->readUint16();
	debug(1, "ANI u7: %d", u7);
	if (u7 != 0)
		warning("ANI u7 non-zero");

	delete aniStream;
}

Common::SeekableSubReadStreamEndian *MohawkEngine_LivingBooks::wrapStreamEndian(uint32 tag, uint16 id) {
	Common::SeekableReadStream *dataStream = getRawData(tag, id);
	return new Common::SeekableSubReadStreamEndian(dataStream, 0, dataStream->size(), isBigEndian(), DisposeAfterUse::YES);
}

Common::String MohawkEngine_LivingBooks::getStringFromConfig(Common::String section, Common::String key) {
	Common::String x;
	_bookInfoFile.getKey(key, section, x);
	return removeQuotesFromString(x);
}

int MohawkEngine_LivingBooks::getIntFromConfig(Common::String section, Common::String key) {
	return atoi(getStringFromConfig(section, key).c_str());
}

Common::String MohawkEngine_LivingBooks::getFileNameFromConfig(Common::String section, Common::String key) {
	Common::String x = getStringFromConfig(section, key);
	return (getPlatform() == Common::kPlatformMacintosh) ? convertMacFileName(x) : convertWinFileName(x);
}

Common::String MohawkEngine_LivingBooks::removeQuotesFromString(Common::String string) {
	// The last char isn't necessarily a quote, the line could have "fade" in it,
	// most likely representing to fade to that page. Hopefully it really is that
	// obvious :P

	// Some versions wrap in quotations, some don't...
	for (uint32 i = 0; i < string.size(); i++) {
		if (string[i] == '\"') {
			string.deleteChar(i);
			i--;
		}
	}

	return string;
}

Common::String MohawkEngine_LivingBooks::convertMacFileName(Common::String string) {
	Common::String filename;

	for (uint32 i = 1; i < string.size(); i++) { // First character should be ignored (another colon)
		if (string[i] == ':')
			filename += '/';
		else
			filename += string[i];
	}

	return filename;
}

Common::String MohawkEngine_LivingBooks::convertWinFileName(Common::String string) {
	Common::String filename;

	for (uint32 i = 0; i < string.size(); i++) {
		if (string[i] == '\\')
			filename += '/';
		else
			filename += string[i];
	}

	return filename;
}

MohawkArchive *MohawkEngine_LivingBooks::createMohawkArchive() const {
	return (getGameType() == GType_LIVINGBOOKSV1) ? new LivingBooksArchive_v1() : new MohawkArchive();
}

} // End of namespace Mohawk