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

#include "common/rect.h"
#include "common/system.h"

#include "chewy/resource.h"
#include "chewy/text.h"

namespace Chewy {

Text::Text() : Resource("atds.tap") {
}

Text::~Text() {
}

TextEntryList *Text::getDialog(uint dialogNum, uint entryNum) {
	if (dialogNum >= kADSTextMax)
		error("getDialog(): Invalid entry number requested, %d (max %d)", dialogNum, kADSTextMax - 1);

	TextEntryList *l = new TextEntryList();

	byte *data = getChunkData(dialogNum);
	byte *ptr = data;

	ptr += 2;	// entry number
	ptr += 2;	// number of persons
	ptr += 2;	// automove count
	ptr += 2;	// cursor number
	ptr += 13;	// misc data

	for (uint i = 0; i <= entryNum; i++) {
		do {
			TextEntry curDialog;
			ptr++;	// current entry
			ptr += 2;
			curDialog.speechId = READ_LE_UINT16(ptr) - VOICE_OFFSET;	ptr += 2;

			do {
				curDialog.text += *ptr++;

				if (*ptr == 0 && *(ptr + 1) != kEndText) {
					// TODO: Split lines
					*ptr = ' ';
				}
			} while (*ptr != kEndText);

			if (i == entryNum)
				l->push_back(curDialog);

		} while (*(ptr + 1) != kEndEntry);

		ptr += 2;	// kEndText, kEndEntry

		if (*ptr == kEndBlock)	// not found
			break;
	}

	delete[] data;

	return l;
}

TextEntry *Text::getText(uint dialogNum, uint entryNum) {
	if (dialogNum < kADSTextMax)
		error("getText(): Invalid entry number requested, %d (min %d)", dialogNum, kADSTextMax);

	TextEntry *d = new TextEntry();
	bool isText = (dialogNum >= kADSTextMax && dialogNum < kADSTextMax + kATSTextMax);
	bool isAutoDialog = (dialogNum >= kADSTextMax + kATSTextMax && dialogNum < kADSTextMax + kATSTextMax + kAADTextMax);
	//bool isInvText = (dialogNum >= kADSTextMax + kATSTextMax + kAADTextMax && dialogNum < kADSTextMax + kATSTextMax + kAADTextMax + kINVTextMax);

	byte *data = getChunkData(dialogNum);
	byte *ptr = data;

	if (isAutoDialog)
		ptr += 3;

	for (uint i = 0; i <= entryNum; i++) {
		ptr += 13;
		d->speechId = READ_LE_UINT16(ptr) - VOICE_OFFSET;	ptr += 2;

		do {
			if (i == entryNum)
				d->text += *ptr++;
			else
				ptr++;

			if (*ptr == 0 && *(ptr + 1) != kEndText) {
				// TODO: Split lines
				*ptr = ' ';
			}
		} while (*ptr);

		if (*(ptr + 1) != kEndText || *(ptr + 2) != kEndChunk) {
			warning("Invalid text resource - %d, %d", dialogNum, entryNum);

			delete[] data;
			delete d;

			return nullptr;
		}

		if (!isText)
			ptr += 3;	// 0, kEndText, kEndChunk
		if (isAutoDialog)
			ptr += 3;

		if (i == entryNum) {
			// Found
			delete[] data;
			return d;
		}
	}

	// Not found
	delete[] data;
	delete d;

	return nullptr;
}

Font::Font(Common::String filename) {
	const uint32 headerFont = MKTAG('T', 'F', 'F', '\0');
	Common::File stream;

	stream.open(filename);

	uint32 header = stream.readUint32BE();

	if (header != headerFont)
		error("Invalid resource - %s", filename.c_str());

	stream.skip(4);	// total memory
	_count = stream.readUint16LE();
	_first = stream.readUint16LE();
	_last = stream.readUint16LE();
	_width = stream.readUint16LE();
	_height = stream.readUint16LE();

	_fontSurface.create(_width * _count, _height, ::Graphics::PixelFormat::createFormatCLUT8());

	byte cur;
	int bitIndex = 7;
	byte *p;

	cur = stream.readByte();

	for (uint n = 0; n < _count; n++) {
		for (uint y = 0; y < _height; y++) {
			p = (byte *)_fontSurface.getBasePtr(n * _width, y);

			for (uint x = n * _width; x < n * _width + _width; x++) {
				*p++ = (cur & (1 << bitIndex)) ? 0 : 0xFF;

				bitIndex--;
				if (bitIndex < 0) {
					bitIndex = 7;
					cur = stream.readByte();
				}
			}
		}
	}
}

Font::~Font() {
	_fontSurface.free();
}

::Graphics::Surface *Font::getLine(const Common::String &text) {
	::Graphics::Surface *line = new ::Graphics::Surface();
	line->create(text.size() * _width, _height, ::Graphics::PixelFormat::createFormatCLUT8());

	for (uint i = 0; i < text.size(); i++) {
		uint x = (text[i] - _first) * _width;
		line->copyRectToSurface(_fontSurface, i * _width, 0, Common::Rect(x, 0, x + _width, _height));
	}

	return line;
}

Common::String ErrorMessage::getErrorMessage(uint num) {
	assert(num < _chunkList.size());

	Chunk *chunk = &_chunkList[num];
	Common::String str;
	byte *data = new byte[chunk->size];

	_stream.seek(chunk->pos, SEEK_SET);
	_stream.read(data, chunk->size);
	if (_encrypted)
		decrypt(data, chunk->size);

	str = (char *)data;
	delete[] data;

	return str;
}

} // End of namespace Chewy