/* 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 "dreamweb/sound.h"
#include "dreamweb/dreamweb.h"

namespace DreamWeb {

void DreamWebEngine::printBoth(const GraphicsFile &charSet, uint16 *x, uint16 y, uint8 c, uint8 nextChar) {
	uint16 newX = *x;
	uint8 width, height;
	printChar(charSet, &newX, y, c, nextChar, &width, &height);
	multiDump(*x, y, width, height);
	*x = newX;
}

uint8 DreamWebEngine::getNextWord(const GraphicsFile &charSet, const uint8 *string, uint8 *totalWidth, uint8 *charCount) {
	*totalWidth = 0;
	*charCount = 0;
	while (true) {
		uint8 firstChar = *string;
		++string;
		++*charCount;
		if ((firstChar == ':') || (firstChar == 0)) { //endall
			*totalWidth += 6;
			return 1;
		}
		if (firstChar == 32) { //endword
			*totalWidth += 6;
			return 0;
		}
		firstChar = modifyChar(firstChar);
		// WORKAROUND: Also filter out invalid characters here (refer to the
		// workaround in printChar() below for more info).
		if (firstChar >= 32 && firstChar != 255) {
			uint8 secondChar = *string;
			uint8 width = charSet._frames[firstChar - 32 + _charShift].width;
			width = kernChars(firstChar, secondChar, width);
			*totalWidth += width;
		}
	}
}

void DreamWebEngine::printChar(const GraphicsFile &charSet, uint16* x, uint16 y, uint8 c, uint8 nextChar, uint8 *width, uint8 *height) {
	// WORKAROUND: Some texts contain leftover tab characters, which will cause
	// OOB memory access when showing a character, as all the printable ones are
	// from 32 onwards. We compensate for that here by ignoring all the invalid
	// characters (0 - 31).
	if (c < 32 || c == 255)
		return;
	
	uint8 dummyWidth, dummyHeight;
	if (width == NULL)
		width = &dummyWidth;
	if (height == NULL)
		height = &dummyHeight;
	if (_foreignRelease)
		y -= 3;
	uint16 tmp = c - 32 + _charShift;
	showFrame(charSet, *x, y, tmp & 0x1ff, (tmp >> 8) & 0xfe, width, height);
	if (_kerning == 0)
		*width = kernChars(c, nextChar, *width);
	(*x) += *width;
}

void DreamWebEngine::printChar(const GraphicsFile &charSet, uint16 x, uint16 y, uint8 c, uint8 nextChar, uint8 *width, uint8 *height) {
	printChar(charSet, &x, y, c, nextChar, width, height);
}

uint8 DreamWebEngine::printSlow(const uint8 *string, uint16 x, uint16 y, uint8 maxWidth, bool centered) {
	_pointerFrame = 1;
	_pointerMode = 3;
	do {
		uint16 offset = x;
		uint16 charCount = getNumber(_charset1, string, maxWidth, centered, &offset);
		do {
			uint8 c0 = string[0];
			uint8 c1 = string[1];
			uint8 c2 = string[2];
			c0 = modifyChar(c0);
			printBoth(_charset1, &offset, y, c0, c1);
			if ((c1 == 0) || (c1 == ':')) {
				return 0;
			}
			if (charCount != 1) {
				c1 = modifyChar(c1);
				_charShift = 91;
				uint16 offset2 = offset;
				printBoth(_charset1, &offset2, y, c1, c2);
				_charShift = 0;
				for (int i=0; i<2; ++i) {
					uint16 mouseState = waitFrames();
					if (_quitRequested)
						return 0;
					if (mouseState == 0)
						continue;
					if (mouseState != _oldButton) {
						return 1;
					}
				}
			}

			++string;
			--charCount;
		} while (charCount);
		y += 10;
	} while (true);
}

uint8 DreamWebEngine::printDirect(const uint8* string, uint16 x, uint16 y, uint8 maxWidth, bool centered) {
	return printDirect(&string, x, &y, maxWidth, centered);
}

uint8 DreamWebEngine::printDirect(const uint8** string, uint16 x, uint16 *y, uint8 maxWidth, bool centered) {
	_lastXPos = x;
	const GraphicsFile &charSet = *_currentCharset;
	while (true) {
		uint16 offset = x;
		uint8 charCount = getNumber(charSet, *string, maxWidth, centered, &offset);
		uint16 i = offset;
		do {
			uint8 c = (*string)[0];
			uint8 nextChar = (*string)[1];
			++(*string);
			if ((c == 0) || (c == ':')) {
				return c;
			}
			c = modifyChar(c);
			uint8 width, height;
			printChar(charSet, &i, *y, c, nextChar, &width, &height);
			_lastXPos = i;
			--charCount;
		} while (charCount);
		*y += _lineSpacing;
	}
}

uint8 DreamWebEngine::getNumber(const GraphicsFile &charSet, const uint8 *string, uint16 maxWidth, bool centered, uint16* offset) {
	uint8 totalWidth = 0;
	uint8 charCount = 0;
	while (true) {
		uint8 wordTotalWidth, wordCharCount;
		uint8 done = getNextWord(charSet, string, &wordTotalWidth, &wordCharCount);
		string += wordCharCount;

		uint16 tmp = totalWidth + wordTotalWidth - 10;
		if (done == 1) { //endoftext
			if (tmp < maxWidth) {
				totalWidth += wordTotalWidth;
				charCount += wordCharCount;
			}

			if (centered) {
				tmp = (maxWidth & 0xfe) + 2 + 20 - totalWidth;
				tmp /= 2;
			} else {
				tmp = 0;
			}
			*offset += tmp;
			return charCount;
		}
		if (tmp >= maxWidth) { //gotoverend
			if (centered) {
				tmp = (maxWidth & 0xfe) - totalWidth + 20;
				tmp /= 2;
			} else {
				tmp = 0;
			}
			*offset += tmp;
			return charCount;
		}
		totalWidth += wordTotalWidth;
		charCount += wordCharCount;
	}
}

uint8 DreamWebEngine::kernChars(uint8 firstChar, uint8 secondChar, uint8 width) {
	if ((firstChar == 'a') || (firstChar == 'u')) {
		if ((secondChar == 'n') || (secondChar == 't') || (secondChar == 'r') || (secondChar == 'i') || (secondChar == 'l'))
			return width-1;
	}
	return width;
}

uint16 DreamWebEngine::waitFrames() {
	readMouse();
	showPointer();
	waitForVSync();
	dumpPointer();
	delPointer();
	return _mouseButton;
}

const char *DreamWebEngine::monPrint(const char *string) {
	_kerning = 1;
	uint16 x = _monAdX;
	const char *iterator = string;
	bool done = false;
	while (!done) {

		uint16 count = getNumber(_monitorCharset, (const uint8 *)iterator, 166, false, &x);
		do {
			char c = *iterator++;
			if (c == ':')
				break;
			if ((c == 0) || (c == '"') || (c == '=')) {
				done = true;
				break;
			}
			if (c == '%') {
				_vars._lastTrigger = *iterator;
				iterator += 2;
				done = true;
				break;
			}
			c = modifyChar(c);
			printChar(_monitorCharset, &x, _monAdY, c, 0, NULL, NULL);
			_cursLocX = x;
			_cursLocY = _monAdY;
			_mainTimer = 1;
			printCurs();
			waitForVSync();
			lockMon();
			delCurs();
		} while (--count);

		x = _monAdX;
		scrollMonitor();
		_cursLocX = _monAdX;
	}

	_kerning = 0;
	return iterator;
}

void DreamWebEngine::rollEndCreditsGameWon() {
	_sound->playChannel0(16, 255);
	_sound->volumeSet(7);
	_sound->volumeChange(0, -1);

	multiGet(_mapStore, 75, 20, 160, 160);

	const uint8 *string = getTextInFile1(3);
	const int linespacing = _lineSpacing;

	for (int i = 0; i < 254; ++i) {
		// Output the text, initially with an offset of 10 pixels,
		// then move it up one pixel until we shifted it by a complete
		// line of text.
		for (int j = 0; j < linespacing; ++j) {
			waitForVSync();
			multiPut(_mapStore, 75, 20, 160, 160);
			waitForVSync();

			// Output up to 18 lines of text
			uint16 y = 10 - j;
			const uint8 *tmp_str = string;
			for (int k = 0; k < 18; ++k) {
				printDirect(&tmp_str, 75, &y, 160 + 1, true);
				y += linespacing;
			}

			waitForVSync();
			multiDump(75, 20, 160, 160);
		}

		// Skip to the next text line
		byte c;
		do {
			c = *string++;
		} while (c != ':' && c != 0);
	}

	hangOn(100);
	panelToMap();
	fadeScreenUpHalf();
}

void DreamWebEngine::rollEndCreditsGameLost() {
	multiGet(_mapStore, 25, 20, 160, 160);

	const uint8 *string = getTextInFile1(49);
	const int linespacing = _lineSpacing;

	for (int i = 0; i < 80; ++i) {
		// Output the text, initially with an offset of 10 pixels,
		// then move it up one pixel until we shifted it by a complete
		// line of text.
		for (int j = 0; j < linespacing; ++j) {
			waitForVSync();
			multiPut(_mapStore, 25, 20, 160, 160);
			waitForVSync();

			// Output up to 18 lines of text
			uint16 y = 10 - j;
			const uint8 *tmp_str = string;
			for (int k = 0; k < 18; ++k) {
				printDirect(&tmp_str, 25, &y, 160 + 1, true);
				y += linespacing;
			}

			waitForVSync();
			multiDump(25, 20, 160, 160);

			if (_lastHardKey == Common::KEYCODE_ESCAPE)
				return;
		}

		// Skip to the next text line
		byte c;
		do {
			c = *string++;
		} while (c != ':' && c != 0);

		if (_lastHardKey == Common::KEYCODE_ESCAPE)
			return;
	}

	hangOne(120);
}

} // End of namespace DreamWeb
