/* 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 "mutationofjb/commands/saycommand.h"

#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/script.h"
#include "mutationofjb/tasks/saytask.h"
#include "mutationofjb/tasks/taskmanager.h"

#include "common/str.h"
#include "common/debug.h"
#include "common/debug-channels.h"

/** @file
 * <firstLine> { <CRLF> <additionalLine> }
 *
 * firstLine ::= ("SM" | "SLM" | "NM" | "NLM") " " <lineToSay> [ "<" <voiceFile> | "<!" ]
 * additionalLine ::= <skipped> " " <lineToSay> ( "<" <voiceFile> | "<!" )
 *
 * Say command comes in four variants: SM, SLM, NM and NLM.
 * Note: In script files, they are usually written as *SM.
 * The asterisk is ignored by the readLine function.
 *
 * Each of them plays a voice file (if present) and/or shows a message
 * (if voice file not present or subtitles are enabled).
 *
 * The difference between versions starting with "S" and "N" is that
 * the "N" version does not show talking animation.
 *
 * The "L" versions are "blocking", i.e. they wait for the previous say command to finish.
 *
 * If the line ends with "<!", it means the message continues to the next line.
 * Next line usually has "SM" (or other variant) repeated, but it does not have to.
 * Then we have the rest of the string to say (which is concatenated with the previous line)
 * and possibly the voice file or "<!" again.
 */

namespace MutationOfJB {

bool SayCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
	bool waitForPrevious = false;
	bool talkingAnimation = false;

	if (line.hasPrefix("SM")) {
		waitForPrevious = false;
		talkingAnimation = true;
	} else if (line.hasPrefix("SLM")) {
		waitForPrevious = true;
		talkingAnimation = true;
	} else if (line.hasPrefix("NM")) {
		waitForPrevious = false;
		talkingAnimation = false;
	} else if (line.hasPrefix("NLM")) {
		waitForPrevious = true;
		talkingAnimation = false;
	} else {
		return false;
	}

	Common::String currentLine = line;

	Common::String lineToSay;
	Common::String voiceFile;

	bool cont = false;
	bool firstPass = true;

	do {
		cont = false;

		uint startPos;
		for (startPos = 0; startPos < currentLine.size(); ++startPos) {
			if (currentLine[startPos] == ' ') {
				break;
			}
		}
		if (startPos == currentLine.size()) {
			if (!firstPass) {
				warning("Unable to parse line '%s'", currentLine.c_str());
				break;
			}
		}
		if (startPos != currentLine.size()) {
			startPos++;
		}

		uint endPos;
		for (endPos = startPos; endPos < currentLine.size(); ++endPos) {
			if (currentLine[endPos] == '<') {
				break;
			}
		}

		Common::String talkStr(currentLine.c_str() + startPos, endPos - startPos);

		if (endPos != currentLine.size()) {
			const char *end = currentLine.c_str() + endPos + 1;
			if (end[0] == '!') {
				cont = true;
			} else {
				voiceFile = end;
			}
		}

		if (talkStr.lastChar() == '~') {
			debug("Found say command ending with '~'. Please take a look.");
		}

		if (lineToSay.empty()) {
			lineToSay = talkStr;
		} else {
			lineToSay += " " + talkStr;
		}

		if (cont) {
			if (!parseCtx.readLine(currentLine)) {
				cont = false;
			}
		}

		firstPass = false;
	} while (cont);

	command = new SayCommand(lineToSay, voiceFile, waitForPrevious, talkingAnimation);

	return true;
}


Command::ExecuteResult SayCommand::execute(ScriptExecutionContext &scriptExecCtx) {
	Game &game = scriptExecCtx.getGame();

	if (_waitForPrevious) {
		if (game.getActiveSayTask()) {
			return InProgress;
		}
	}

	TaskPtr task(new SayTask(_lineToSay, game.getGameData()._color));
	game.getTaskManager().startTask(task);

	return Finished;
}

Common::String SayCommand::debugString() const {
	return Common::String::format("SHOWMSG%s%s '%s' '%s'", _waitForPrevious ? "+WAIT" : "", _talkingAnimation ? "+TALKANIM" : "", _lineToSay.c_str(), _voiceFile.c_str());
}

}