/* 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 "kyra/text_mr.h"
#include "kyra/screen_mr.h"
#include "kyra/resource.h"

namespace Kyra {

TextDisplayer_MR::TextDisplayer_MR(KyraEngine_MR *vm, Screen_MR *screen)
	: TextDisplayer(vm, screen), _vm(vm), _screen(screen) {
}

char *TextDisplayer_MR::preprocessString(const char *str) {
	if (_talkBuffer != str) {
		assert(strlen(str) < sizeof(_talkBuffer) - 1);
		strcpy(_talkBuffer, str);
	}

	char *p = _talkBuffer;
	while (*p) {
		if (*p++ == '\r')
			return _talkBuffer;
	}

	p = _talkBuffer;
	Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
	_screen->_charWidth = -2;

	const int maxTextWidth = (_vm->language() == 0) ? 176 : 240;
	int textWidth = _screen->getTextWidth(p);

	if (textWidth > maxTextWidth) {
		int count = 0, offs = 0;
		if (textWidth > (3*maxTextWidth)) {
			count = getCharLength(p, textWidth/4);
			offs = dropCRIntoString(p, count, getCharLength(p, maxTextWidth));
			p += count + offs;
			// No update of textWidth here
		}

		if (textWidth > (2*maxTextWidth)) {
			count = getCharLength(p, textWidth/3);
			offs = dropCRIntoString(p, count, getCharLength(p, maxTextWidth));
			p += count + offs;
			textWidth = _screen->getTextWidth(p);
		}

		count = getCharLength(p, textWidth/2);
		offs = dropCRIntoString(p, count, getCharLength(p, maxTextWidth));
		p += count + offs;
		textWidth = _screen->getTextWidth(p);

		if (textWidth > maxTextWidth) {
			count = getCharLength(p, textWidth/2);
			offs = dropCRIntoString(p, count, getCharLength(p, maxTextWidth));
		}
	}

	_screen->setFont(curFont);
	return _talkBuffer;
}

int TextDisplayer_MR::dropCRIntoString(char *str, int minOffs, int maxOffs) {
	int offset = 0;
	char *proc = str + minOffs;

	for (int i = minOffs; i < maxOffs; ++i) {
		if (*proc == ' ') {
			*proc = '\r';
			return offset;
		} else if (*proc == '-') {
			memmove(proc+1, proc, strlen(proc)+1);
			*(++proc) = '\r';
			++offset;
			return offset;
		}

		++offset;
		++proc;

		if (!*proc)
			return 0;
	}

	offset = 0;
	proc = str + minOffs;
	for (int i = minOffs; i >= 0; --i) {
		if (*proc == ' ') {
			*proc = '\r';
			return offset;
		} else if (*proc == '-') {
			memmove(proc+1, proc, strlen(proc)+1);
			*(++proc) = '\r';
			++offset;
			return offset;
		}

		--offset;
		--proc;

		if (!*proc)
			return 0;
	}

	*(str + minOffs) = '\r';
	return 0;
}

void TextDisplayer_MR::printText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2) {
	if (_vm->_albumChatActive) {
		c0 = 0xEE;
		c1 = 0xE3;
		c2 = 0x00;
	}

	uint8 colorMap[] = { 0, 255, 240, 240 };
	colorMap[3] = c1;
	_screen->setTextColor(colorMap, 0, 3);
	_screen->_charWidth = -2;
	_screen->printText(str, x, y, c0, c2);
	_screen->_charWidth = 0;
}

void TextDisplayer_MR::restoreScreen() {
	_vm->restorePage3();
	_vm->drawAnimObjects();
	_screen->hideMouse();
	_screen->copyRegion(_talkCoords.x, _talkMessageY, _talkCoords.x, _talkMessageY, _talkCoords.w, _talkMessageH, 2, 0, Screen::CR_NO_P_CHECK);
	_screen->showMouse();
	_vm->flagAnimObjsForRefresh();
	_vm->refreshAnimObjects(0);
}

void TextDisplayer_MR::calcWidestLineBounds(int &x1, int &x2, int w, int x) {
	x1 = x;
	x1 -= (w >> 1);
	x2 = x1 + w + 1;

	if (x1 + w >= 311)
		x1 = 311 - w - 1;

	if (x1 < 8)
		x1 = 8;

	x2 = x1 + w + 1;
}

#pragma mark -

int KyraEngine_MR::chatGetType(const char *str) {
	while (*str)
		++str;
	--str;
	switch (*str) {
	case '!':
		return 2;

	case ')':
		return 3;

	case '?':
		return 1;

	case '.':
	default:
		return 0;
	}
}

int KyraEngine_MR::chatCalcDuration(const char *str) {
	return MAX<int>(120, strlen(str)*6);
}

void KyraEngine_MR::objectChat(const char *str, int object, int vocHigh, int vocLow) {
	if (_mainCharacter.animFrame == 87 || _mainCharacter.animFrame == 0xFFFF || _mainCharacter.x1 <= 0 || _mainCharacter.y1 <= 0)
		return;

	_chatVocLow = _chatVocHigh = -1;
	objectChatInit(str, object, vocHigh, vocLow);
	_chatText = str;
	_chatObject = object;
	int chatType = chatGetType(str);

	if (_mainCharacter.facing > 7)
		_mainCharacter.facing = 5;

	static const uint8 talkScriptTable[] = {
		0x10, 0x11, 0x12, 0x13,
		0x0C, 0x0D, 0x0E, 0x0F,
		0x0C, 0x0D, 0x0E, 0x0F,
		0x04, 0x05, 0x06, 0x07,
		0x00, 0x01, 0x02, 0x03,
		0x00, 0x01, 0x02, 0x03,
		0x08, 0x09, 0x0A, 0x0B,
		0x08, 0x09, 0x0A, 0x0B
	};

	static const char *talkFilenameTable[] = {
		"MTFL00S.EMC", "MTFL00Q.EMC", "MTFL00E.EMC", "MTFL00T.EMC",
		"MTFR00S.EMC", "MTFR00Q.EMC", "MTFR00E.EMC", "MTFR00T.EMC",
		 "MTL00S.EMC",  "MTL00Q.EMC",  "MTL00E.EMC",  "MTL00T.EMC",
		 "MTR00S.EMC",  "MTR00Q.EMC",  "MTR00E.EMC",  "MTR00T.EMC",
		 "MTA00S.EMC",  "MTA00Q.EMC",  "MTA00E.EMC",  "MTA00T.EMC"
	};

	int chat = talkScriptTable[chatType + _mainCharacter.facing * 4];
	objectChatProcess(talkFilenameTable[chat]);
	_text->restoreScreen();
	_mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing];
	updateCharacterAnim(0);
	_chatText = 0;
	_chatObject = -1;
	setNextIdleAnimTimer();
}

void KyraEngine_MR::objectChatInit(const char *str, int object, int vocHigh, int vocLow) {
	str = _text->preprocessString(str);
	int lineNum = _text->buildMessageSubstrings(str);

	int xPos = 0, yPos = 0;

	if (!object) {
		int scale = getScale(_mainCharacter.x1, _mainCharacter.y1);
		yPos = _mainCharacter.y1 - ((_mainCharacter.height * scale) >> 8) - 8;
		xPos = _mainCharacter.x1;
	} else {
		yPos = _talkObjectList[object].y;
		xPos = _talkObjectList[object].x;
	}

	yPos -= lineNum * 10;
	yPos = MAX(yPos, 0);
	_text->_talkMessageY = yPos;
	_text->_talkMessageH = lineNum*10;

	int width = _text->getWidestLineWidth(lineNum);
	_text->calcWidestLineBounds(xPos, yPos, width, xPos);
	_text->_talkCoords.x = xPos;
	_text->_talkCoords.w = width + 2;

	restorePage3();

	_screen->hideMouse();

	_chatTextEnabled = textEnabled();
	if (_chatTextEnabled) {
		objectChatPrintText(str, object);
		_chatEndTime = _system->getMillis() + chatCalcDuration(str) * _tickLength;
	} else {
		_chatEndTime = _system->getMillis();
	}

	if (speechEnabled()) {
		_chatVocHigh = vocHigh;
		_chatVocLow = vocLow;
	} else {
		_chatVocHigh = _chatVocLow = -1;
	}

	_screen->showMouse();
}

void KyraEngine_MR::objectChatPrintText(const char *str, int object) {
	int c1 = _talkObjectList[object].color;
	str = _text->preprocessString(str);
	int lineNum = _text->buildMessageSubstrings(str);
	int maxWidth = _text->getWidestLineWidth(lineNum);
	int x = (object == 0) ? _mainCharacter.x1 : _talkObjectList[object].x;
	int cX1 = 0, cX2 = 0;
	_text->calcWidestLineBounds(cX1, cX2, maxWidth, x);

	for (int i = 0; i < lineNum; ++i) {
		str = &_text->_talkSubstrings[i*_text->maxSubstringLen()];

		int y = _text->_talkMessageY + i * 10;
		x = _text->getCenterStringX(str, cX1, cX2);

		_text->printText(str, x, y, c1, 0xF0, 0);
	}
}

void KyraEngine_MR::objectChatProcess(const char *script) {
	memset(&_chatScriptData, 0, sizeof(_chatScriptData));
	memset(&_chatScriptState, 0, sizeof(_chatScriptState));

	_emc->load(script, &_chatScriptData, &_opcodesAnimation);
	_emc->init(&_chatScriptState, &_chatScriptData);
	_emc->start(&_chatScriptState, 0);
	while (_emc->isValid(&_chatScriptState))
		_emc->run(&_chatScriptState);

	if (_chatVocHigh >= 0) {
		playVoice(_chatVocHigh, _chatVocLow);
		_chatVocHigh = _chatVocLow = -1;
	}

	_useFrameTable = true;
	objectChatWaitToFinish();
	_useFrameTable = false;

	_emc->unload(&_chatScriptData);
}

void KyraEngine_MR::objectChatWaitToFinish() {
	int charAnimFrame = _mainCharacter.animFrame;
	setCharacterAnimDim(_animShapeWidth, _animShapeHeight);

	_emc->init(&_chatScriptState, &_chatScriptData);
	_emc->start(&_chatScriptState, 1);

	bool running = true;
	const uint32 endTime = _chatEndTime;
	resetSkipFlag();

	while (running && !shouldQuit()) {
		if (!_emc->isValid(&_chatScriptState))
			_emc->start(&_chatScriptState, 1);

		_animNeedUpdate = false;
		while (!_animNeedUpdate && _emc->isValid(&_chatScriptState) && !shouldQuit()) {
			musicUpdate(0);
			_emc->run(&_chatScriptState);
		}

		int curFrame = _animNewFrame;
		uint32 delayTime = _animDelayTime;

		_mainCharacter.animFrame = curFrame;
		updateCharacterAnim(0);

		uint32 nextFrame = _system->getMillis() + delayTime * _tickLength;

		while (_system->getMillis() < nextFrame && !shouldQuit()) {
			updateWithText();

			const uint32 curTime = _system->getMillis();
			if ((textEnabled() && !speechEnabled() && curTime > endTime) || (speechEnabled() && !snd_voiceIsPlaying()) || skipFlag()) {
				snd_stopVoice();
				resetSkipFlag();
				nextFrame = curTime;
				running = false;
			}

			delay(10);
		}
	}

	_mainCharacter.animFrame = charAnimFrame;
	updateCharacterAnim(0);
	resetCharacterAnimDim();
}

void KyraEngine_MR::badConscienceChat(const char *str, int vocHigh, int vocLow) {
	if (!_badConscienceShown)
		return;

	setNextIdleAnimTimer();
	_chatVocHigh = _chatVocLow = -1;
	objectChatInit(str, 1, vocHigh, vocLow);
	_chatText = str;
	_chatObject = 1;
	badConscienceChatWaitToFinish();
	updateSceneAnim(0x0E, _badConscienceFrameTable[_badConscienceAnim+16]);
	_text->restoreScreen();
	update();
	_chatText = 0;
	_chatObject = -1;
}

void KyraEngine_MR::badConscienceChatWaitToFinish() {
	if (_chatVocHigh) {
		playVoice(_chatVocHigh, _chatVocLow);
		_chatVocHigh = _chatVocLow = -1;
	}

	bool running = true;
	const uint32 endTime = _chatEndTime;
	resetSkipFlag();

	uint32 nextFrame = _system->getMillis() + _rnd.getRandomNumberRng(4, 8) * _tickLength;

	int frame = _badConscienceFrameTable[_badConscienceAnim+24];
	while (running && !shouldQuit()) {
		if (nextFrame < _system->getMillis()) {
			++frame;
			if (_badConscienceFrameTable[_badConscienceAnim+32] < frame)
				frame = _badConscienceFrameTable[_badConscienceAnim+24];

			updateSceneAnim(0x0E, frame);
			updateWithText();

			nextFrame = _system->getMillis() + _rnd.getRandomNumberRng(4, 8) * _tickLength;
		}

		updateWithText();

		const uint32 curTime = _system->getMillis();
		if ((textEnabled() && !speechEnabled() && curTime > endTime) || (speechEnabled() && !snd_voiceIsPlaying()) || skipFlag()) {
			snd_stopVoice();
			resetSkipFlag();
			nextFrame = curTime;
			running = false;
		}

		delay(10);
	}
}

void KyraEngine_MR::goodConscienceChat(const char *str, int vocHigh, int vocLow) {
	if (!_goodConscienceShown)
		return;

	setNextIdleAnimTimer();
	_chatVocHigh = _chatVocLow = -1;
	objectChatInit(str, 87, vocHigh, vocLow);
	_chatText = str;
	_chatObject = 87;
	goodConscienceChatWaitToFinish();
	updateSceneAnim(0x0F, _goodConscienceFrameTable[_goodConscienceAnim+10]);
	_text->restoreScreen();
	update();
	_chatText = 0;
	_chatObject = -1;
}

void KyraEngine_MR::goodConscienceChatWaitToFinish() {
	if (_chatVocHigh) {
		playVoice(_chatVocHigh, _chatVocLow);
		_chatVocHigh = _chatVocLow = -1;
	}

	bool running = true;
	const uint32 endTime = _chatEndTime;
	resetSkipFlag();

	uint32 nextFrame = _system->getMillis() + _rnd.getRandomNumberRng(3, 6) * _tickLength;

	int frame = _goodConscienceFrameTable[_goodConscienceAnim+15];
	while (running && !shouldQuit()) {
		if (nextFrame < _system->getMillis()) {
			++frame;
			if (_goodConscienceFrameTable[_goodConscienceAnim+20] < frame)
				frame = _goodConscienceFrameTable[_goodConscienceAnim+15];

			updateSceneAnim(0x0F, frame);
			updateWithText();

			nextFrame = _system->getMillis() + _rnd.getRandomNumberRng(3, 6) * _tickLength;
		}

		updateWithText();

		const uint32 curTime = _system->getMillis();
		if ((textEnabled() && !speechEnabled() && curTime > endTime) || (speechEnabled() && !snd_voiceIsPlaying()) || skipFlag()) {
			snd_stopVoice();
			resetSkipFlag();
			nextFrame = curTime;
			running = false;
		}

		delay(10);
	}
}

void KyraEngine_MR::albumChat(const char *str, int vocHigh, int vocLow) {
	_talkObjectList[1].x = 190;
	_talkObjectList[1].y = 188;

	_chatVocHigh = _chatVocLow = -1;
	_albumChatActive = true;
	albumChatInit(str, 1, vocHigh, vocLow);
	_albumChatActive = false;

	_chatText = str;
	_chatObject = 1;
	_screen->hideMouse();
	albumChatWaitToFinish();
	_screen->showMouse();

	_chatText = 0;
	_chatObject = -1;
}

void KyraEngine_MR::albumChatInit(const char *str, int object, int vocHigh, int vocLow) {
	Common::String realString;

	while (*str) {
		if (str[0] == '\\' && str[1] == 'r') {
			realString += '\r';
			++str;
		} else {
			realString += *str;
		}

		++str;
	}

	str = realString.c_str();

	str = _text->preprocessString(str);
	int lineNum = _text->buildMessageSubstrings(str);

	int xPos = 0, yPos = 0;

	if (!object) {
		int scale = getScale(_mainCharacter.x1, _mainCharacter.y1);
		yPos = _mainCharacter.y1 - ((_mainCharacter.height * scale) >> 8) - 8;
		xPos = _mainCharacter.x1;
	} else {
		yPos = _talkObjectList[object].y;
		xPos = _talkObjectList[object].x;
	}

	yPos -= lineNum * 10;
	yPos = MAX(yPos, 0);
	_text->_talkMessageY = yPos;
	_text->_talkMessageH = lineNum*10;

	int width = _text->getWidestLineWidth(lineNum);
	_text->calcWidestLineBounds(xPos, yPos, width, xPos);
	_text->_talkCoords.x = xPos;
	_text->_talkCoords.w = width + 2;

	_screen->hideMouse();

	if (textEnabled()) {
		objectChatPrintText(str, object);
		_chatEndTime = _system->getMillis() + chatCalcDuration(str) * _tickLength;
	} else {
		_chatEndTime = _system->getMillis();
	}

	if (speechEnabled()) {
		_chatVocHigh = vocHigh;
		_chatVocLow = vocLow;
	} else {
		_chatVocHigh = _chatVocLow = -1;
	}

	_screen->showMouse();
}

void KyraEngine_MR::albumChatWaitToFinish() {
	if (_chatVocHigh) {
		playVoice(_chatVocHigh, _chatVocLow);
		_chatVocHigh = _chatVocLow = -1;
	}

	bool running = true;
	const uint32 endTime = _chatEndTime;
	resetSkipFlag();

	uint32 nextFrame = 0;
	int frame = 12;
	while (running && !shouldQuit()) {
		if (nextFrame < _system->getMillis()) {
			++frame;
			if (frame > 22)
				frame = 13;

			albumRestoreRect();
			_album.wsa->displayFrame(frame, 2, -100, 90, 0x4000, 0, 0);
			albumUpdateRect();

			nextFrame = _system->getMillis() + _rnd.getRandomNumberRng(4, 8) * _tickLength;
		}

		if (_album.nextPage != 14)
			albumUpdateAnims();
		else
			_screen->updateScreen();

		const uint32 curTime = _system->getMillis();
		if ((textEnabled() && !speechEnabled() && curTime > endTime) || (speechEnabled() && !snd_voiceIsPlaying()) || skipFlag()) {
			snd_stopVoice();
			resetSkipFlag();
			nextFrame = curTime;
			running = false;
		}

		delay(10);
	}
}

void KyraEngine_MR::malcolmSceneStartupChat() {
	if (_noStartupChat)
		return;

	int index = _mainCharacter.sceneId - _chapterLowestScene[_currentChapter];
	if (_newSceneDlgState[index])
		return;

	updateDlgBuffer();
	int vocHighBase = 0, vocHighIndex = 0, index1 = 0, index2 = 0;
	loadDlgHeader(vocHighBase, vocHighIndex, index1, index2);

	_cnvFile->seek(index1*6, SEEK_CUR);
	_cnvFile->seek(index2*4, SEEK_CUR);
	_cnvFile->seek(index*2, SEEK_CUR);
	_cnvFile->seek(_cnvFile->readUint16LE(), SEEK_SET);

	_isStartupDialog = true;
	processDialog(vocHighIndex, vocHighBase, 0);
	_isStartupDialog = false;
	_newSceneDlgState[index] = true;
}

void KyraEngine_MR::updateDlgBuffer() {
	char dlgFile[16];
	char cnvFile[16];

	if (_cnvFile)
		_cnvFile->seek(0, SEEK_SET);

	if (_curDlgIndex == _mainCharacter.dlgIndex && _curDlgChapter == _currentChapter && _curDlgLang == _lang)
		return;

	snprintf(dlgFile, 16, "CH%.02d-S%.02d.", _currentChapter, _mainCharacter.dlgIndex);
	appendLanguage(dlgFile, _lang, 16);
	snprintf(cnvFile, 16, "CH%.02d-S%.02d.CNV", _currentChapter, _mainCharacter.dlgIndex);

	delete _cnvFile;
	delete _dlgBuffer;

	_res->exists(cnvFile, true);
	_res->exists(dlgFile, true);
	_cnvFile = _res->createReadStream(cnvFile);
	_dlgBuffer = _res->createReadStream(dlgFile);
	assert(_cnvFile);
	assert(_dlgBuffer);
}

void KyraEngine_MR::loadDlgHeader(int &vocHighBase, int &vocHighIndex, int &index1, int &index2) {
	assert(_cnvFile);
	vocHighIndex = _cnvFile->readSint16LE();
	vocHighBase = _cnvFile->readSint16LE();
	index1 = _cnvFile->readSint16LE();
	index2 = _cnvFile->readSint16LE();
}

void KyraEngine_MR::setDlgIndex(int index) {
	if (_mainCharacter.dlgIndex != index) {
		memset(_newSceneDlgState, 0, sizeof(_newSceneDlgState));
		memset(_conversationState, -1, sizeof(_conversationState));
		_chatAltFlag = false;
		_mainCharacter.dlgIndex = index;
	}
}

void KyraEngine_MR::updateDlgIndex() {
	uint16 dlgIndex = _mainCharacter.dlgIndex;

	if (_currentChapter == 1) {
		static const uint8 dlgIndexMoodNice[] = { 0x0C, 0x0E, 0x10, 0x0F, 0x11 };
		static const uint8 dlgIndexMoodNormal[] = { 0x00, 0x02, 0x04, 0x03, 0x05 };
		static const uint8 dlgIndexMoodEvil[] = { 0x06, 0x08, 0x0A, 0x09, 0x0B };

		if (_malcolmsMood == 0)
			dlgIndex = dlgIndexMoodNice[_characterShapeFile];
		else if (_malcolmsMood == 1)
			dlgIndex = dlgIndexMoodNormal[_characterShapeFile];
		else if (_malcolmsMood == 2)
			dlgIndex = dlgIndexMoodEvil[_characterShapeFile];
	} else if (_currentChapter == 2) {
		if (dlgIndex >= 8)
			dlgIndex -= 4;
		if (dlgIndex >= 4)
			dlgIndex -= 4;

		if (_malcolmsMood == 0)
			dlgIndex += 8;
		else if (_malcolmsMood == 2)
			dlgIndex += 4;
	} else if (_currentChapter == 4) {
		if (dlgIndex >= 10)
			dlgIndex -= 5;
		if (dlgIndex >= 5)
			dlgIndex -= 5;

		if (_malcolmsMood == 0)
			dlgIndex += 10;
		else if (_malcolmsMood == 2)
			dlgIndex += 5;
	}

	_mainCharacter.dlgIndex = dlgIndex;
}

void KyraEngine_MR::processDialog(int vocHighIndex, int vocHighBase, int funcNum) {
	bool running = true;
	int script = -1;
	int vocHigh = -1, vocLow = -1;

	while (running) {
		uint16 cmd = _cnvFile->readUint16LE();
		int object = cmd - 12;

		if (cmd == 10) {
			break;
		} else if (cmd == 4) {
			vocHighBase = _cnvFile->readUint16LE();
			setDlgIndex(vocHighBase);
		} else if (cmd == 11) {
			int strSize = _cnvFile->readUint16LE();
			vocLow = _cnvFile->readUint16LE();
			_cnvFile->read(_stringBuffer, strSize);
			_stringBuffer[strSize] = 0;
		} else {
			vocHigh = _vocHighTable[vocHighIndex-1] + vocHighBase;
			vocLow = _cnvFile->readUint16LE();
			getTableEntry(_dlgBuffer, vocLow, _stringBuffer);

			if (_isStartupDialog) {
				delay(60*_tickLength, true);
				_isStartupDialog = false;
			}

			if (*_stringBuffer == 0)
				continue;

			if (cmd != 12) {
				if (object != script) {
					if (script >= 0) {
						dialogEndScript(script);
						script = -1;
					}

					dialogStartScript(object, funcNum);
					script = object;
				}

				npcChatSequence(_stringBuffer, object, vocHigh, vocLow);
			} else {
				if (script >= 0) {
					dialogEndScript(script);
					script = -1;
				}

				objectChat(_stringBuffer, 0, vocHigh, vocLow);
				playStudioSFX(_stringBuffer);
			}
		}
	}

	if (script != -1)
		dialogEndScript(script);
}

void KyraEngine_MR::dialogStartScript(int object, int funcNum) {
	_dialogSceneAnim = _talkObjectList[object].sceneAnim;
	_dialogSceneScript = _talkObjectList[object].sceneScript;
	if (_dialogSceneAnim >= 0 && _dialogSceneScript >= 0) {
		_specialSceneScriptStateBackup[_dialogSceneScript] = _specialSceneScriptState[_dialogSceneScript];
		_specialSceneScriptState[_dialogSceneScript] = true;
	}

	_emc->init(&_dialogScriptState, &_dialogScriptData);
	_emc->load(_talkObjectList[object].filename, &_dialogScriptData, &_opcodesDialog);

	_dialogScriptFuncStart = funcNum * 3 + 0;
	_dialogScriptFuncProc = funcNum * 3 + 1;
	_dialogScriptFuncEnd = funcNum * 3 + 2;

	_emc->start(&_dialogScriptState, _dialogScriptFuncStart);
	while (_emc->isValid(&_dialogScriptState))
		_emc->run(&_dialogScriptState);
}

void KyraEngine_MR::dialogEndScript(int object) {
	_emc->init(&_dialogScriptState, &_dialogScriptData);
	_emc->start(&_dialogScriptState, _dialogScriptFuncEnd);

	while (_emc->isValid(&_dialogScriptState))
		_emc->run(&_dialogScriptState);

	if (_dialogSceneAnim >= 0 && _dialogSceneScript >= 0) {
		_specialSceneScriptState[_dialogSceneScript] = _specialSceneScriptStateBackup[_dialogSceneScript];
		_dialogSceneScript = _dialogSceneAnim = -1;
	}

	_emc->unload(&_dialogScriptData);
}

void KyraEngine_MR::npcChatSequence(const char *str, int object, int vocHigh, int vocLow) {
	_chatText = str;
	_chatObject = object;
	_chatVocHigh = _chatVocLow = -1;

	objectChatInit(str, object, vocHigh, vocLow);

	if (_chatVocHigh >= 0 && _chatVocLow >= 0) {
		playVoice(_chatVocHigh, _chatVocLow);
		_chatVocHigh = _chatVocLow = -1;
	}

	_emc->init(&_dialogScriptState, &_dialogScriptData);
	_emc->start(&_dialogScriptState, _dialogScriptFuncProc);

	resetSkipFlag();

	uint32 endTime = _chatEndTime;
	bool running = true;
	while (running && !shouldQuit()) {
		if (!_emc->run(&_dialogScriptState)) {
			_emc->init(&_dialogScriptState, &_dialogScriptData);
			_emc->start(&_dialogScriptState, _dialogScriptFuncProc);
		}

		const uint32 curTime = _system->getMillis();
		if ((textEnabled() && !speechEnabled() && curTime > endTime) || (speechEnabled() && !snd_voiceIsPlaying()) || skipFlag()) {
			snd_stopVoice();
			resetSkipFlag();
			running = false;
		}
	}
	_text->restoreScreen();
	_chatText = 0;
	_chatObject= - 1;
}

void KyraEngine_MR::randomSceneChat() {
	updateDlgBuffer();

	int index = (_mainCharacter.sceneId - _chapterLowestScene[_currentChapter]) * 2;

	int vocHighBase = 0, vocHighIndex = 0, index1 = 0, index2 = 0;
	loadDlgHeader(vocHighBase, vocHighIndex, index1, index2);

	if (_chatAltFlag)
		index++;
	_chatAltFlag = !_chatAltFlag;

	_cnvFile->seek(index1*6, SEEK_CUR);
	_cnvFile->seek(index*2, SEEK_CUR);
	_cnvFile->seek(_cnvFile->readUint16LE(), SEEK_SET);

	processDialog(vocHighIndex, vocHighBase, 0);
}

void KyraEngine_MR::runDialog(int dlgIndex, int funcNum) {
	switch (_currentChapter-2) {
	case 0:
		dlgIndex -= 34;
		break;

	case 1:
		dlgIndex -= 54;
		break;

	case 2:
		dlgIndex -= 55;
		break;

	case 3:
		dlgIndex -= 70;
		break;

	default:
		break;
	}

	updateDlgBuffer();

	int vocHighBase = 0, vocHighIndex = 0, index1 = 0, index2 = 0;
	loadDlgHeader(vocHighBase, vocHighIndex, index1, index2);

	int convState = _conversationState[dlgIndex][vocHighBase];
	uint32 offset = ((vocHighIndex == 1) ? dlgIndex - 1 : dlgIndex) * 6;
	if (convState == -1) {
		_cnvFile->seek(offset, SEEK_CUR);
		_conversationState[dlgIndex][vocHighBase] = 0;
	} else if (convState == 0 || convState == 2) {
		_cnvFile->seek(offset+2, SEEK_CUR);
		_conversationState[dlgIndex][vocHighBase] = 1;
	} else {
		_cnvFile->seek(offset+4, SEEK_CUR);
		_conversationState[dlgIndex][vocHighBase] = 2;
	}

	_cnvFile->seek(_cnvFile->readUint16LE(), SEEK_SET);

	processDialog(vocHighIndex, vocHighBase, funcNum);
}

} // End of namespace Kyra