/* 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 "titanic/true_talk/tt_sentence.h"
#include "titanic/true_talk/tt_concept.h"
#include "titanic/true_talk/script_handler.h"
#include "titanic/titanic.h"

namespace Titanic {

TTsentenceConcept *TTsentenceConcept::addSibling() {
	if (_nextP != nullptr)
		// This should never happen
		return nullptr;

	TTsentenceConcept *nextP = new TTsentenceConcept();
	_nextP = nextP;
	return nextP;
}

/*------------------------------------------------------------------------*/

TTsentence::TTsentence(int inputCtr, const TTstring &line, CScriptHandler *owner,
		TTroomScript *roomScript, TTnpcScript *npcScript) :
		_owner(owner), _field2C(1), _inputCtr(inputCtr), _field34(0),
		_field38(0), _initialLine(line), _nodesP(nullptr), _roomScript(roomScript),
		_npcScript(npcScript), _field58(0), _field5C(0) {
	_status = _initialLine.isValid() && _normalizedLine.isValid() ? SS_11: SS_VALID;
}

TTsentence::TTsentence(const TTsentence *src) : _sentenceConcept(src->_sentenceConcept),
		_initialLine(src->_initialLine), _normalizedLine(src->_normalizedLine) {
	copyFrom(*src);
}

TTsentence::~TTsentence() {
	_sentenceConcept.deleteSiblings();

	if (_nodesP) {
		_nodesP->deleteSiblings();
		delete _nodesP;
	}
}

void TTsentence::copyFrom(const TTsentence &src) {
	if (!src.getStatus())
		_status = SS_5;
	else if (!src._initialLine.isValid() || !src._normalizedLine.isValid())
		_status = SS_11;
	else
		_status = SS_VALID;

	_inputCtr = src._inputCtr;
	_owner = src._owner;
	_roomScript = src._roomScript;
	_npcScript = src._npcScript;
	_field58 = src._field58;
	_field5C = src._field5C;
	_field34 = src._field34;
	_field38 = src._field38;
	_field2C = src._field2C;
	_nodesP = nullptr;

	if (src._nodesP) {
		// Source has processed nodes, so duplicate them
		for (TTsentenceNode *node = src._nodesP; node;
				node = dynamic_cast<TTsentenceNode *>(node->_nextP)) {
			TTsentenceNode *newNode = new TTsentenceNode(node->_wordP);
			if (_nodesP)
				_nodesP->addToTail(newNode);
			else
				_nodesP = newNode;
		}
	}
}

int TTsentence::storeVocabHit(TTword *word) {
	if (!word)
		return 0;

	TTsentenceNode *node = new TTsentenceNode(word);
	if (_nodesP) {
		_nodesP->addToTail(node);
	} else {
		_nodesP = node;
	}

	return 0;
}

bool TTsentence::fn1(const CString &str, int wordId1, const CString &str1, const CString &str2,
		const CString &str3, int wordId2, int val1, int val2, const TTconceptNode *node) const {
	if (node)
		node = &_sentenceConcept;

	if (!node && !node)
		return false;
	if (val1 && !is18(val1, node))
		return false;
	if (!str.empty() && !fn2(0, str, node))
		return false;
	if (wordId1 && !fn4(1, wordId1, node))
		return false;
	if (!str1.empty() && !fn2(2, str1, node))
		return false;
	if (!str2.empty() && !fn2(3, str2, node))
		return false;
	if (!str3.empty() && !fn2(4, str3, node))
		return false;
	if (wordId2 && !fn4(5, wordId2, node))
		return false;
	if (val2 && !is1C(val2, node))
		return false;

	return true;
}

bool TTsentence::fn3(const CString &str1, const CString &str2, const CString &str3,
		const CString &str4, const CString &str5, const CString &str6,
		int val1, int val2, const TTconceptNode *node) const {
	if (!node)
		node = &_sentenceConcept;

	if (val1 && !is18(val1, node))
		return false;
	if (!str1.empty() && !fn2(0, str1, node))
		return false;
	if (!str2.empty() && !fn2(1, str2, node))
		return false;
	if (!str3.empty() && !fn2(2, str3, node))
		return false;
	if (!str4.empty() && !fn2(3, str4, node))
		return false;
	if (!str5.empty() && !fn2(4, str5, node))
		return false;
	if (!str6.empty() && !fn2(5, str6, node))
		return false;
	if (!val2 && !is1C(val2, node))
		return false;

	return true;
}

bool TTsentence::fn2(int slotIndex, const TTstring &str, const TTconceptNode *node) const {
	if (!node)
		node = &_sentenceConcept;
	TTconcept *concept = getFrameSlot(slotIndex, node);

	if (!concept)
		return str == "isEmpty";

	bool abortFlag = false;
	switch (concept->_scriptType) {
	case 1:
		if (str == "thePlayer")
			abortFlag = 1;
		break;

	case 2:
		if (str == "targetNpc")
			abortFlag = 1;
		break;

	case 3:
		if (str == "otherNpc")
			abortFlag = 1;
		break;

	default:
		break;
	}

	TTstring conceptText = concept->getText();
	if (abortFlag || str == conceptText || concept->compareTo(str)) {
		delete concept;
		return true;
	}

	if (slotIndex == 1 && g_vm->_exeResources._owner->_concept4P) {
		if (str == g_vm->_exeResources._owner->_concept4P->getText() &&
				conceptText == "do")
			goto exit;
	}

	if (g_vm->_exeResources._owner->_concept2P && (slotIndex == 0 ||
			slotIndex == 3 || slotIndex == 4)) {
		if (str == g_vm->_exeResources._owner->_concept2P->getText() &&
				(conceptText == "it" || conceptText == "he" || conceptText == "she" ||
				conceptText == "him" || conceptText == "her" || conceptText == "them" ||
				conceptText == "they"))
			goto exit;
	}

	if (g_vm->_exeResources._owner->_concept1P && (slotIndex == 0 ||
		slotIndex == 2 || slotIndex == 3 || slotIndex == 4 || slotIndex == 5)) {
		if (str == g_vm->_exeResources._owner->_concept2P->getText() &&
			(conceptText == "it" || conceptText == "that" || conceptText == "he" ||
				conceptText == "she" || conceptText == "him" || conceptText == "her" ||
				conceptText == "them" || conceptText == "they" || conceptText == "those" ||
				conceptText == "1" || conceptText == "thing"))
			goto exit;
	}

	if (g_vm->_exeResources._owner->_concept1P && (slotIndex == 0 || slotIndex == 2)) {
		if (conceptText == "?" && str == g_vm->_exeResources._owner->_concept2P->getText()) {
			delete concept;
			concept = getFrameSlot(5, node);
			conceptText = concept->getText();

			if (conceptText == "it" || conceptText == "that" || conceptText == "he" ||
				conceptText == "she" || conceptText == "him" || conceptText == "her" ||
				conceptText == "them" || conceptText == "they" || conceptText == "those" ||
				conceptText == "1" || conceptText == "thing")
				abortFlag = true;
		}
	}

exit:
	delete concept;
	return abortFlag;
}

bool TTsentence::fn4(int mode, int wordId, const TTconceptNode *node) const {
	if (!node)
		node = &_sentenceConcept;

	switch (mode) {
	case 1:
		return node->_concept1P && node->_concept1P->getWordId() == wordId;

	case 5:
		return node->_concept5P && node->_concept5P->getWordId() == wordId;

	default:
		return false;
	}
}

TTconcept *TTsentence::getFrameEntry(int slotIndex, const TTconceptNode *conceptNode) const {
	if (!conceptNode)
		conceptNode = &_sentenceConcept;

	return conceptNode->_concepts[slotIndex];
}

TTconcept *TTsentence::getFrameSlot(int slotIndex, const TTconceptNode *conceptNode) const {
	TTconcept *newConcept = new TTconcept();
	TTconcept *concept = getFrameEntry(slotIndex, conceptNode);
	newConcept->copyFrom(concept);

	if (!newConcept->isValid()) {
		delete newConcept;
		newConcept = nullptr;
	}

	return newConcept;
}

bool TTsentence::isFrameSlotClass(int slotIndex, WordClass wordClass, const TTconceptNode *conceptNode) const {
	TTconcept *concept = getFrameEntry(slotIndex, conceptNode);
	if (concept && concept->_wordP) {
		return concept->_wordP->isClass(wordClass);
	} else {
		return false;
	}
}

int TTsentence::is18(int val, const TTconceptNode *node) const {
	if (!node)
		node = &_sentenceConcept;
	return node->_field18 == val;
}

int TTsentence::is1C(int val, const TTconceptNode *node) const {
	if (!node)
		node = &_sentenceConcept;
	return node->_field1C == val;
}

bool TTsentence::isConcept34(int slotIndex, const TTconceptNode *node) const {
	TTconcept *concept = getFrameEntry(slotIndex, node);
	return concept && concept->getState();
}

bool TTsentence::localWord(const char *str) const {
	CScriptHandler &scriptHandler = *g_vm->_exeResources._owner;
	bool foundMatch = false;

	if (scriptHandler._concept1P) {
		TTstring s = scriptHandler._concept1P->getText();
		if (s == str)
			foundMatch = true;
	} else if (scriptHandler._concept2P) {
			TTstring s = scriptHandler._concept2P->getText();
			if (s == str)
				foundMatch = true;
	}

	int val = g_vm->_exeResources.get18();
	bool result = false;

	for (TTsentenceNode *nodeP = _nodesP; nodeP && !result;
			nodeP = dynamic_cast<TTsentenceNode *>(nodeP->_nextP)) {
		TTsynonym syn;
		if (!nodeP->_wordP)
			continue;

		const TTstring wordStr = nodeP->_wordP->_text;
		if (val == 3 && wordStr == str) {
			result = true;
		} else if (nodeP->_wordP->findSynByName(str, &syn, val)) {
			result = true;
		} else if (foundMatch) {
			result = wordStr == "it" || wordStr == "that" || wordStr == "he"
				|| wordStr == "she" || wordStr == "him" || wordStr == "her"
				|| wordStr == "them" || wordStr == "they" || wordStr == "those"
				|| wordStr == "1" || wordStr == "thing";
		}
	}

	return result;
}

bool TTsentence::contains(const char *str) const {
	return _initialLine.contains(str) || _normalizedLine.contains(str);
}

} // End of namespace Titanic