// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.


#include "mission_compiler.h"
#include "step.h"
#include "nel/misc/factory.h"
#include <memory>

using namespace std;
using namespace NLMISC;
using namespace NLLIGO;


// *****
// IStep
// *****

IStep *IStep::createStep(CMissionData &md, IPrimitive *prim)
{
	string *c;
	if (!prim->getPropertyByName("class", c))
		throw EParseException(prim, "Can't find property 'class' on primitive");

	IStep *ret = CFactoryIndirect<IStepFactory, string>::instance().getFactory(*c)->createStep(md, prim);
	ret->init(md, prim);

	return ret;
};

IStep::IStep(CMissionData &md, NLLIGO::IPrimitive *prim)
	:	_Primitive(prim),
	EndOfBranch(false),
	JumpPoint(false)
{
	if (prim == NULL)
		return;

	prim->getPropertyByName("class", _StepType);
	prim->getPropertyByName("name", _StepName);

	// parse the sub prim to create action & objectives;
	IPrimitive *child;
	// parse the preactions
	{
		TPrimitiveClassAndNamePredicate pred("actions", "pre_actions");
		child = getPrimitiveChild(prim, pred);
	}

	if (child)
	{
		for (uint i=0; i<child->getNumChildren(); ++i)
		{
			IPrimitive *subChild;
			child->getChild(subChild, i);
			if (subChild)
			{
				// ok, we got one
				IStepContent *sc = IStepContent::createStepContent(md, subChild);
				if (sc)
					_PreActions.push_back(sc);
			}
		}
	}
	// parse the objectives
	{
		TPrimitiveClassAndNamePredicate pred("mission_objectives", "objectives");
		child = getPrimitiveChild(prim, pred);
	}
	if (child)
	{
		for (uint i=0; i<child->getNumChildren(); ++i)
		{
			IPrimitive *subChild;
			child->getChild(subChild, i);
			if (subChild)
			{
				// ok, we got one
				IStepContent *sc = IStepContent::createStepContent(md, subChild);
				if (sc)
					_Objectives.push_back(sc);
			}
		}
	}
	// parse the post actions
	{
		TPrimitiveClassAndNamePredicate pred("actions", "post_actions");
		child = getPrimitiveChild(prim, pred);
	}
	if (child)
	{
		for (uint i=0; i<child->getNumChildren(); ++i)
		{
			IPrimitive *subChild;
			child->getChild(subChild, i);
			if (subChild)
			{
				// ok, we got one
				IStepContent *sc = IStepContent::createStepContent(md, subChild);
				if (sc)
					_PostActions.push_back(sc);
			}
		}
	}
}

void IStep::fillStepJump(CMissionData &md, std::set<TJumpInfo> &jumpPoints)
{
	uint i;
	// extract step content jump points
	for (i=0; i<_PreActions.size(); ++i)
	{
		_PreActions[i]->fillJump(md, jumpPoints);
	}
	for (i=0; i<_Objectives.size(); ++i)
	{
		_Objectives[i]->fillJump(md, jumpPoints);
	}
	for (i=0; i<_PostActions.size(); ++i)
	{
		_PostActions[i]->fillJump(md, jumpPoints);
	}

	fillJump(md, jumpPoints);

	for (i = 0; i < _SubSteps.size(); ++i)
		if (_SubSteps[i] != NULL)
			_SubSteps[i]->fillStepJump(md, jumpPoints);
}


void IStep::fillJump(CMissionData &md, std::set<TJumpInfo> &jumpPoints)
{
	// jump to the next step
	IStep *next = md.getNextStep(this);

	if (!EndOfBranch && next)
	{
		TJumpInfo ji(next->getStepName());
		jumpPoints.insert(ji);
	}
	else
	{
		TJumpInfo ji("__end__");
		jumpPoints.insert(ji);
	}
}

std::string IStep::genCode(CMissionData &md)
{
	string ret;

	ret += genCodePreActions(md);
	ret += genCodeObjectives(md);
	ret += genCodePostActions(md);

	return ret;
}

std::string  IStep::genCodePreActions(CMissionData &md)
{
	string ret;
	// generate code for pre actions
	for (uint i=0; i<_PreActions.size(); ++i)
	{
		ret += _PreActions[i]->genStepContentCode(md);
	}
	return ret;
}

std::string  IStep::genCodeObjectives(CMissionData &md)
{
	string ret;
	// generate code for objectives
	for (uint i=0; i<_Objectives.size(); ++i)
	{
		ret += _Objectives[i]->genStepContentCode(md);
	}
	return ret;
}

std::string  IStep::genCodePostActions(CMissionData &md)
{
	string ret;
	//generate code for post actions
	for (uint i=0; i<_PostActions.size(); ++i)
	{
		ret += _PostActions[i]->genStepContentCode(md);
	}
	return ret;
}


std::string IStep::genPhrase()
{
	string ret;
	// generate code for pre actions
	for (uint i=0; i<_PreActions.size(); ++i)
	{
		ret += _PreActions[i]->genStepContentPhrase();
	}
	// generate code for objectives
	for (uint i=0; i<_Objectives.size(); ++i)
	{
		ret += _Objectives[i]->genStepContentPhrase();
	}
	//generate code for post actions
	for (uint i=0; i<_PostActions.size(); ++i)
	{
		ret += _PostActions[i]->genStepContentPhrase();
	}
	//generate code for sub steps
	for (uint i=0; i<_SubSteps.size(); ++i)
	{
		ret += _SubSteps[i]->genPhrase();
	}
	
	return ret;
}


class CStep : public IStep
{
public:
	CStep(CMissionData &md, IPrimitive *prim)
		: IStep(md, prim)
	{
		//  nothing special to do
	}

};
REGISTER_STEP_INDIRECT(CStep, "step");

class CStepObjective : public CStep
{
public:
	virtual void getPredefParam(uint32 &numEntry, CPhrase::TPredefParams &predef) 
	{
		numEntry = 0;
		predef.clear();
	};

	CStepObjective(CMissionData &md, IPrimitive *prim, const std::string &prefix = "")
		: CStep(md, prim),
		_HideObj(false),
		_Prefix(prefix)
	{
	}

	void init(CMissionData &md, IPrimitive *prim)
	{
		_HideObj = md.getProperty(prim, _Prefix + "hide_obj", true, false) == "true";
		_OverloadObj = md.getPropertyArray(prim, _Prefix + "overload_objective", false, false);
		_RoleplayObj = md.getPropertyArray(prim, _Prefix + "roleplay_objective", false, false);
		uint32 numEntry;
		CPhrase::TPredefParams params;
		// ask derived class for predefined params
		getPredefParam(numEntry, params);
		_OverloadPhrase.initPhrase(md, prim, _OverloadObj, numEntry, params);
		_RoleplayPhrase.initPhrase(md, prim, _RoleplayObj, numEntry, params);
	}
	
	virtual bool isAction() { return true; }

	string genCode(CMissionData &md)
	{
		string ret;
		if (_HideObj)
			ret = "hide_obj" + NL;
		if (!_OverloadObj.empty())
		{
			ret += "set_obj : " + _OverloadPhrase.genScript(md) + NL;
		}
		if (!_RoleplayObj.empty())
		{
			ret += "set_obj_rp : " + _RoleplayPhrase.genScript(md) + NL;
		}

		return ret;
	}

	string genPhrase()
	{
		string ret;
		ret += _OverloadPhrase.genPhrase();
		ret += _RoleplayPhrase.genPhrase();

		ret += IStep::genPhrase();

		return ret;
	}
	
protected:
	string			_Prefix;
	bool			_HideObj;
	vector<string>	_OverloadObj;
	CPhrase			_OverloadPhrase;
	vector<string>	_RoleplayObj;
	CPhrase			_RoleplayPhrase;
};

class CStepDynChatTalkTo : public CStepObjective
{
//	string		_BotNameVar;
//	string		_BotName;
	TCompilerVarName	_BotName;
	CPhrase		_Phrase;

	void getPredefParam(uint32 &numEntry, CPhrase::TPredefParams &predef)
	{
		numEntry = 0;
		predef.resize(1);
		predef[0].resize(1);
		predef[0][0] = _BotName.getParamInfo();
//		if (_BotNameVar.empty())
//			predef[0][0] = CPhrase::TParamInfo("npc", STRING_MANAGER::bot);
//		else
//			predef[0][0] = CPhrase::TParamInfo(_BotNameVar, STRING_MANAGER::bot);
	}
public:
	CStepDynChatTalkTo(CMissionData &md, IPrimitive *prim, const std::string &prefix = "")
		: CStepObjective(md, prim, prefix)
	{
	}

	void init(CMissionData &md, IPrimitive *prim)
	{
		_BotName.init("npc", STRING_MANAGER::bot, md, prim, "npc_name");
//		_BotNameVar = md.getProperty(prim, "npc_name", false, false);
		// remove the variable tag if any
//		untagVar(_BotNameVar);

//		_BotName = md.getProperty(prim, "npc_name", true, false);
		vector<string> vs;
		vs = md.getPropertyArray(prim, "talk_to_menu", false, false);
		CPhrase::TPredefParams pp(1);
		pp[0].push_back(_BotName.getParamInfo());
//		if (_BotNameVar.empty())
//			pp[0].push_back(CPhrase::TParamInfo("npc", STRING_MANAGER::bot));
//		else
//			pp[0].push_back(CPhrase::TParamInfo(_BotNameVar, STRING_MANAGER::bot));
		_Phrase.initPhrase(md, prim, vs, 0, pp);
 
//		_Phrase.initPhrase(md, prim, vs);

//		if (_Phrase.asAdditionnalParams())
//		{
//			// we need to remove the default 'npc' parameter if add params are found
//			CPhrase temp;
//			temp.initPhrase(md, prim, vs);
//
//			_Phrase = temp;
//		}
		CStepObjective::init(md, prim);
	}

	string genCode(CMissionData &md)
	{
		string ret;
		ret = CStepObjective::genCode(md);

		ret += "talk_to : "+_BotName._VarValue;
		
		if (!_Phrase.isEmpty())
			ret += " : "+_Phrase.genScript(md);
		ret += NL;
		
		return ret;
	}

	string genPhrase()
	{
		string ret;
		ret = CStepObjective::genPhrase();
		ret += _Phrase.genPhrase();

		return ret;
	}
};

class CStepJump : public IStep
{
public:
	CStepJump(CMissionData &md, IPrimitive *prim)
		: IStep(md, prim)
	{
		_StepName = md.getProperty(prim, "target", true, false);
	}

	void fillJump(CMissionData &md, set<TJumpInfo> &jumpPoints)
	{
		jumpPoints.insert(TJumpInfo(_StepName, "", false));
	}

	string genCode(CMissionData &md)
	{
		string ret;

		ret += "jump : " + _StepName + NL;

		return ret;
	}

	bool isAJump()
	{
		return true;
	}

	string		_StepName;
};
REGISTER_STEP_INDIRECT(CStepJump, "jump_to");

/// pseudo step used in step parsing 
class CStepEnd : public IStep
{
public:
	CStepEnd(CMissionData &md, IPrimitive *prim)
		: IStep(md, prim)
	{
	}

	string genCode(CMissionData &md)
	{
		string ret;

		ret = "end"+NL;

		return ret;
	}

	bool isEnd() { return true; }

};
REGISTER_STEP_INDIRECT(CStepEnd, "end");

class CStepFailure : public IStep
{
	// Optional jump at end of failure
	string		_JumpTo;
public:
	CStepFailure(CMissionData &md, IPrimitive *prim)
		: IStep(md, prim)
	{
		// parse the sub prim to create action & objectives;
		IPrimitive *child;
		// parse the pre-actions
		{
			TPrimitiveClassAndNamePredicate pred("actions", "actions");
			child = getPrimitiveChild(prim, pred);
		}
		if (child)
		{
			for (uint i=0; i<child->getNumChildren(); ++i)
			{
				IPrimitive *subChild;
				child->getChild(subChild, i);
				if (subChild)
				{
					// ok, we got one
					IStepContent *sc = IStepContent::createStepContent(md, subChild);
					if (sc)
						_PreActions.push_back(sc);
				}
			}
		}
		// look for an optional jump
		{
			TPrimitiveClassPredicate pred("jump_to");
			child = getPrimitiveChild(prim, pred);
		}
		if (child)
		{
			// ok, we have a jump at end of fail step
			_JumpTo = md.getProperty(child, "target", true, false);
		}
	}

	string genCode(CMissionData &md)
	{
		string ret;

		ret = "failure" + NL;

		string tmp = IStep::genCode(md);
		tabulateLine(tmp, 1);
		ret += tmp;


		if (!_JumpTo.empty())
		{
			ret += "\tjump : "+_JumpTo + NL;
		}

		ret += "/failure"+NL;

		return ret;
	}

	void fillJump(CMissionData &md, set<TJumpInfo> &jumpPoints)
	{
		IStep::fillJump(md, jumpPoints);
		if (!_JumpTo.empty())
			jumpPoints.insert(TJumpInfo(_JumpTo));
	}

};
REGISTER_STEP_INDIRECT(CStepFailure, "step_failure");

class CStepCrash : public IStep
{
	// Optional jump at end of failure
	string			_JumpTo;
	vector<string>	_AiInstances;
public:
	CStepCrash(CMissionData &md, IPrimitive *prim)
		: IStep(md, prim)
	{
		// parse the sub prim to create action & objectives;
		IPrimitive *child;
		// parse the pre-actions
		{
			TPrimitiveClassAndNamePredicate pred("actions", "actions");
			child = getPrimitiveChild(prim, pred);
		}
		if (child)
		{
			for (uint i=0; i<child->getNumChildren(); ++i)
			{
				IPrimitive *subChild;
				child->getChild(subChild, i);
				if (subChild)
				{
					// ok, we got one
					IStepContent *sc = IStepContent::createStepContent(md, subChild);
					if (sc)
						_PreActions.push_back(sc);
				}
			}
		}

		// parse ai instance list
		vector<string> vs = md.getPropertyArray(prim, "ai_instances", true, false);
/*		if (vs.size() == 0)
		{
			string err = toString("crash block need at least one ai instance !");
			throw EParseException(prim, err.c_str());
		}
*/
		for (uint i=0; i<vs.size(); ++i)
		{
			_AiInstances.push_back(vs[i]);
		}

		// look for an optional jump
		{
			TPrimitiveClassPredicate pred("jump_to");
			child = getPrimitiveChild(prim, pred);
		}
		if (child)
		{
			// ok, we have a jump at end of fail step
			_JumpTo = md.getProperty(child, "target", true, false);
		}
	}

	string genCode(CMissionData &md)
	{
		string ret;

		ret = "crash";

		if (!_AiInstances.empty())
		{
			ret += " :";
			for (uint i=0; i<_AiInstances.size(); ++i)
			{
				ret += string(" ")+_AiInstances[i];
			}
		}
		
		ret += NL;

		string tmp = IStep::genCode(md);
		tabulateLine(tmp, 1);
		ret += tmp;


		if (!_JumpTo.empty())
		{
			ret += "\tjump : "+_JumpTo + NL;
		}

		ret += "/crash"+NL;

		return ret;
	}

	void fillJump(CMissionData &md, set<TJumpInfo> &jumpPoints)
	{
		IStep::fillJump(md, jumpPoints);
		if (!_JumpTo.empty())
			jumpPoints.insert(TJumpInfo(_JumpTo));
	}

};
REGISTER_STEP_INDIRECT(CStepCrash, "step_crash");

// ///////////////////// //
// STEP PLAYER RECONNECT //
// ///////////////////// //

// ***************************************************************************
CStepPlayerReconnect::CStepPlayerReconnect(CMissionData &md, IPrimitive *prim) : IStep(md, prim)
{
	// parse the sub prim to create action & objectives;
	IPrimitive *child;

	TPrimitiveSet resp;
	{
		TPrimitivePropertyPredicate pred("step_tag", "true");
		filterPrimitiveChilds(prim, pred, resp);
	}
	for (uint i=0; i<resp.size(); ++i)
	{
		_SubBranchs.push_back(resp[i]);
	}

	// look for an optional jump
	{
		TPrimitiveClassPredicate pred("jump_to");
		child = getPrimitiveChild(prim, pred);
	}
	if (child)
	{
		// ok, we have a jump at end of fail step
		_JumpTo = md.getProperty(child, "target", true, false);
	}
}

// ***************************************************************************
TPrimitiveSet CStepPlayerReconnect::getSubBranchs()
{
	return _SubBranchs;
}

// ***************************************************************************
string CStepPlayerReconnect::genCode(CMissionData &md)
{
	string ret;

	ret = "player_reconnect"+NL;

	string tmp = IStep::genCode(md);
	tabulateLine(tmp, 1);
	ret += tmp;

	for (uint i = 0; i < _SubSteps.size(); ++i)
	{
		if ( md.isThereAJumpTo(_SubSteps[i]->getStepName()) )
		{
			// insert a jump point
			ret += "jump_point : " + _SubSteps[i]->getStepName() + NL;
		}

		ret += _SubSteps[i]->genCode(md);
	}

	if (!_JumpTo.empty())
	{
		ret += "\tjump : "+_JumpTo + NL;
	}

	ret += "/player_reconnect"+NL;

	return ret;
}

// ***************************************************************************
void CStepPlayerReconnect::fillJump(CMissionData &md, set<TJumpInfo> &jumpPoints)
{
	IStep::fillJump(md, jumpPoints);
	if (!_JumpTo.empty())
		jumpPoints.insert(TJumpInfo(_JumpTo));
}

REGISTER_STEP_INDIRECT(CStepPlayerReconnect, "step_player_reconnect");

// //////////////////////////// //
// END OF STEP PLAYER RECONNECT //
// //////////////////////////// //

// ***************************************************************************
// get the jump point of a node. NB: if the node is itself a jump, then directly jump to its target
static std::string *getJumpTarget(IPrimitive *child)
{
	if(!child)
		return NULL;

	// default: jump to the node
	string *s= NULL;
	child->getPropertyByName("name", s);

	// if the node is a jump itself
	string *className= NULL;
	child->getPropertyByName("class", className);
	if (className && *className == "jump_to")
	{
		// generate jump to the step jump target
		child->getPropertyByName("target", s);
	}
	
	return s;
}


// ***************************************************************************
class CStepDynChat : public IStep
{
public:
	CStepDynChat(CMissionData &md, IPrimitive *prim)
		: IStep(md, prim)
	{
		_BotName = md.getProperty(prim, "npc_name", true, false);
		vector<string> temp = md.getPropertyArray(prim, "phrase", false, false);
		_Phrase.initPhrase(md, prim, temp);
		// talk_to menu
		temp = md.getPropertyArray(prim, "talk_to_menu", false, false);
//		_TalkToMenu.initPhrase(md, prim, temp);

		_TalkToObjective = NULL;
		std::auto_ptr<CStepDynChatTalkTo> talkToObjective; // next calls could throw exceptions, so take care...
		if (!temp.empty())
		{
			talkToObjective.reset(new CStepDynChatTalkTo(md, prim, "talk_to_"));
			talkToObjective->init(md, prim);
		}

		// build the sub branch list
		IPrimitive *noResp;
		{
			TPrimitiveClassPredicate pred("no_answer");
			noResp = getPrimitiveChild(prim, pred);
		}
		nlassert(noResp);
		_SubBranchs.push_back(noResp);
		
		TPrimitiveSet resp;
		{
			TPrimitiveClassPredicate pred("dyn_answer");
			filterPrimitiveChilds(prim, pred, resp);
		}
		_Responses.resize(resp.size());
		for (uint i=0; i<resp.size(); ++i)
		{
			_SubBranchs.push_back(resp[i]);
			if (resp[i]->getNumChildren() == 0)
				throw EParseException(prim, "missing step under 'dyn_answer' node for response");

			vector<string> temp = md.getPropertyArray(resp[i], "phrase_answer", false, false);
			_Responses[i].initPhrase(md, resp[i], temp);
		}
		_TalkToObjective = talkToObjective.release(); // commit result
	}
	~CStepDynChat()
	{
		delete _TalkToObjective;
	}
	
	TPrimitiveSet	getSubBranchs() 
	{
		TPrimitiveSet vStepsToReturn;
		for (uint i = 0; i < _SubBranchs.size(); ++i)
		{
			TPrimitiveSet childs;
			{
				TPrimitivePropertyPredicate pred("step_tag", "true");
				filterPrimitiveChilds(_SubBranchs[i], pred, childs);
			}
			for (uint j = 0; j < childs.size(); ++j)
				vStepsToReturn.push_back(childs[j]);
		}
		return vStepsToReturn;
	}
	
	void fillJump(CMissionData &md, set<TJumpInfo> &jumpPoints)
	{
		for (uint i=0; i<_SubBranchs.size(); ++i)
		{
			string *s;
			IPrimitive *firstStep;
			if (!_SubBranchs[i]->getChild(firstStep, 0))
				throw EParseException(_Primitive, toString("Can't find child in %uth sub branch", i+1).c_str());

			bool ret = firstStep->getPropertyByName("name", s);
			if (!ret)
				throw EParseException(_Primitive, toString("Can't find property 'name' in %uth sub branch", i+1).c_str());

			string label;
			_SubBranchs[i]->getPropertyByName("name", label);
			jumpPoints.insert(TJumpInfo(*s, label));
		}
	}

	string genCode(CMissionData &md)
	{
		string ret;

		// if there's a talk_to menu, add it
//		if (!_TalkToMenu.isEmpty())
		if (_TalkToObjective != NULL)
		{			
			ret += _TalkToObjective->genCode(md);
//			ret += "talk_to : "+_BotName;	
//			ret += " : " + _TalkToMenu.genScript(md);
//			ret += NL;
		}


		ret += "dyn_chat : " + _BotName + " : " + _Phrase.genScript(md);

		for (uint i=1; i<_SubBranchs.size(); ++i)
		{
			IPrimitive *child;
			_SubBranchs[i]->getChild(child, 0);

			// get the jump target
			string *s= getJumpTarget(child);

			ret += " : " + *s + " " + _Responses[i-1].genScript(md);
		}

		ret += NL;

		return ret;
	}

	string genPhrase()
	{
		string ret; /* = CStepObjective::genPhrase();*/

//		if (!_TalkToMenu.isEmpty())
		if (_TalkToObjective != NULL)
		{			
			ret += _TalkToObjective->genPhrase();
//			ret += _TalkToMenu.genPhrase();
		}

		ret += _Phrase.genPhrase();

		for (uint i=0; i<_Responses.size(); ++i)
		{
			ret += _Responses[i].genPhrase();
		}
		return ret;
	}

	/// the list of sub branch for the dyn chat.
	TPrimitiveSet	_SubBranchs;
	// phrase for the main text
	CPhrase			_Phrase;
	// list of phrase for each response
	vector<CPhrase>	_Responses;

	string	_BotName;

	// 'talk_to' part
//	CPhrase			_TalkToMenu;
	CStepDynChatTalkTo	*_TalkToObjective;	
	
};
REGISTER_STEP_INDIRECT(CStepDynChat, "step_dyn_chat");

class CLinearDynChat : public IStep
{
	enum TLinearMode
	{
		mode_fail,
		mode_retry,
		mode_continue,
//		mode_skipable
	};

	TLinearMode	_Mode;
	string		_BotName;
	CPhrase		_Phrase;
	CPhrase		_PhraseYes;

	
public:


	CLinearDynChat(CMissionData &md, IPrimitive *prim)
		: IStep(md, prim)
	{
		_BotName = md.getProperty(prim, "npc_name", true, false);
		vector<string> temp = md.getPropertyArray(prim, "phrase", false, false);
		_Phrase.initPhrase(md, prim, temp);

		// set the linear mode
		string className;
		prim->getPropertyByName("class", className);

		if (className == "linear_dyn_chat_fail")
			_Mode = mode_fail;
		else if (className == "linear_dyn_chat_retry")
			_Mode = mode_retry;
		else if (className == "linear_dyn_chat_continue")
			_Mode = mode_continue;
		else
		{
			string err = toString("Unknow class '%s' for dyn chat !", className.c_str());
			throw EParseException(prim, err.c_str());
		}

		// read the response phrase
		vector<string> phraseYes;
		phraseYes = md.getPropertyArray(prim, "phrase_yes", false, false);
		_PhraseYes.initPhrase(md, prim, phraseYes);
	}
	
	string genCode(CMissionData &md)
	{
		string ret;

		switch (_Mode)
		{
		case mode_fail:
			ret = "dyn_chat : "+_BotName+" : "+_Phrase.genScript(md)+" : "+_StepName+"_resp "+_PhraseYes.genScript(md)+NL;
			ret += "fail"+NL;
			ret += "jump_point : " +_StepName+"_resp"+NL;
			break;
		case mode_retry:
			ret += "jump_point : " +_StepName+"_retry"+NL;
			ret += "dyn_chat : "+_BotName+" : "+_Phrase.genScript(md)+" : "+_StepName+"_resp "+_PhraseYes.genScript(md)+NL;
			ret += "jump : "+_StepName+"_retry"+NL;
			ret += "jump_point : " +_StepName+"_resp"+NL;
			break;
		case mode_continue:
			ret = "dyn_chat : "+_BotName+" : "+_Phrase.genScript(md)+" : "+_StepName+"_resp "+_PhraseYes.genScript(md)+NL;
			ret += "jump_point : " +_StepName+"_resp"+NL;
			break;
		}

		return ret;
	}

	string genPhrase()
	{
		string ret;
//		ret = CStepObjective::genPhrase();
		ret += _Phrase.genPhrase();
		ret += _PhraseYes.genPhrase();

		return ret;
	}

};
typedef CLinearDynChat TLinearDynChatFail;
REGISTER_STEP_INDIRECT(TLinearDynChatFail, "linear_dyn_chat_fail");
typedef CLinearDynChat TLinearDynChatRetry;
REGISTER_STEP_INDIRECT(TLinearDynChatRetry, "linear_dyn_chat_retry");
typedef CLinearDynChat TLinearDynChatContinue;
REGISTER_STEP_INDIRECT(TLinearDynChatContinue, "linear_dyn_chat_continue");

// /////// //
// STEP IF //
// /////// //

CStepIf::CStepIf(CMissionData &md, IPrimitive *prim) : IStep(md, prim)
{
	if (prim->getNumChildren() != 2)
		throw EParseException(prim, toString("step_if need two child primitive for 'not_ok' and 'ok' clause, found %u child", prim->getNumChildren()).c_str());

	IPrimitive *notOk = const_cast<IPrimitive *>(prim->getPrimitive("result_no"));
	//prim->getChild(notOk, 0);
	IPrimitive *ok = const_cast<IPrimitive *>(prim->getPrimitive("result_yes"));
	//prim->getChild(ok, 1);
	
	if (notOk == NULL)
		throw EParseException(prim, "Can't find 'not ok' step branch");
	if (ok == NULL)
		throw EParseException(prim, "Can't find 'ok' step branch");

	string name;
	notOk->getPropertyByName("class", name);
	if (name != "result_no")
		throw EParseException(prim, toString("first child must be of class 'result_no', found '%s' instead", name.c_str()).c_str());
	ok->getPropertyByName("class", name);
	if (name != "result_yes")
		throw EParseException(prim, toString("second child must be of class 'result_yes', found '%s' instead", name.c_str()).c_str());
	
	// push in order : not ok, then ok
	_SubBranchs.push_back(notOk);
	_SubBranchs.push_back(ok);


	string s = md.getProperty(prim, "condition_type", true, false);
	if (s == "test_missions_done")
		_IfType = it_mission_done;
	else if (s == "test_skills_level")
		_IfType = it_skills;
	else if (s == "test_bricks_known")
		_IfType = it_bricks;
	else if (s == "test_sdb")
		_IfType = it_sdb;
	else if (s == "test_race")
		_IfType = it_race;
	else if (s == "test_cult")
		_IfType = it_cult;
	else if (s == "test_civ")
		_IfType = it_civ;
	else if (s == "test_faction_point")
		_IfType = it_faction_point;
	else if (s == "test_guild_civ")
		_IfType = it_guild_civ;
	else if (s == "test_guild_cult")
		_IfType = it_guild_cult;
	else if (s == "test_guild_fame")
		_IfType = it_guild_fame;
	else if (s == "test_no_trial")
		_IfType = it_no_trial;
	else if (s == "test_item_in_inv")
		_IfType = it_item_in_inv;
	else
		throw EParseException(prim, "Unknow test type !");

	_IfParams = md.getPropertyArray(prim, "test_parameters", true, false);
}

TPrimitiveSet CStepIf::getSubBranchs() 
{
	TPrimitiveSet vStepsToReturn;
	for (uint i = 0; i < _SubBranchs.size(); ++i)
	{
		TPrimitiveSet childs;
		{
			TPrimitivePropertyPredicate pred("step_tag", "true");
			filterPrimitiveChilds(_SubBranchs[i], pred, childs);
		}
		for (uint j = 0; j < childs.size(); ++j)
			vStepsToReturn.push_back(childs[j]);
	}
	return vStepsToReturn;
}

void CStepIf::fillJump(CMissionData &md, set<TJumpInfo> &jumpPoints)
{
	/*string *s;
	TPrimitiveSet childs;
	bool ret;
	
	filterPrimitiveChilds(_SubBranchs[1], TPrimitivePropertyPredicate("step_tag", "true"), childs);
	if (!childs.empty())
	{
		ret = childs[0]->getPropertyByName("name", s);
		if (!ret)
			throw EParseException(childs[0], "Can't find property 'name' in first step of 'ok' sub branch");

		jumpPoints.insert(TJumpInfo(*s, "yes"));
	}

	// insert link to 'no' step
	childs.clear();
	filterPrimitiveChilds(_SubBranchs[0], TPrimitivePropertyPredicate("step_tag", "true"), childs);
	if (!childs.empty())
	{
		ret = childs[0]->getPropertyByName("name", s);
		if (!ret)
			throw EParseException(childs[0], "Can't find property 'name' in first step of 'not ok' sub branch");

		jumpPoints.insert(TJumpInfo(*s, "no"));
	}*/
}

string CStepIf::genCode(CMissionData &md)
{
	string ret;

	uint32 i;

	TPrimitiveSet childs;
	vector<IStep*> yesSteps;
	vector<IStep*> noSteps;

	// Get the 'yes branch' jump point
	{
		TPrimitivePropertyPredicate pred("step_tag", "true");
		filterPrimitiveChilds(_SubBranchs[1], pred, childs);
	}
	if (!childs.empty())
	{
		for (i = 0; i < _SubSteps.size(); ++i)
			if (std::find(childs.begin(), childs.end(), _SubSteps[i]->getPrimitive()) != childs.end())
				yesSteps.push_back(_SubSteps[i]);

		if (yesSteps.empty())
		{
			string err = toString("In step '%s', can't find sub step in 'yes' sub branch", getStepName().c_str());
			throw EParseException(_SubBranchs[1]->getParent(), err.c_str());
		}
	}

	// Get the 'no branch' jump point
	childs.clear();
	{
		TPrimitivePropertyPredicate pred("step_tag", "true");
		filterPrimitiveChilds(_SubBranchs[0], pred, childs);
	}
	if (!childs.empty())
	{
		for (i = 0; i < _SubSteps.size(); ++i)
			if (std::find(childs.begin(), childs.end(), _SubSteps[i]->getPrimitive()) != childs.end())
				noSteps.push_back(_SubSteps[i]);

		if (noSteps.empty())
		{
			string err = toString("In step '%s', can't find sub step in 'no' sub branch", getStepName().c_str());
			throw EParseException(_SubBranchs[0]->getParent(), err.c_str());
		}
	}

	if ((yesSteps.empty()) && (noSteps.empty()))
	{
		string err = toString("In step '%s', no yes-branch and no no-branch", getStepName().c_str());
		throw EParseException(_SubBranchs[0]->getParent(), err.c_str());
	}

	string jumpToYes;
	if (!yesSteps.empty())
		jumpToYes = yesSteps[0]->getStepName();
	else
		jumpToYes = getStepName() + "_endif";


	// type of IF:
	switch(_IfType)
	{
	case it_mission_done:
		if (_IfParams.empty())
			throw EParseException(_Primitive, "if_mission_done need at least one mission to test");
		for (uint i=0; i<_IfParams.size(); ++i)
			ret += "if_mission_done : " + _IfParams[i] + " : " + jumpToYes + NL;
		break;
	case it_skills:
		if (_IfParams.empty())
			throw EParseException(_Primitive, "if_skills need at least one <skill level> to test");
		ret = "if_skills : ";
		for (uint i=0; i<_IfParams.size(); ++i)
		{
			ret += _IfParams[i];
			if (i < _IfParams.size()-1)
				ret += "; ";
		}
		ret += " : " + jumpToYes + NL;
		break;
	case it_bricks:
		if (_IfParams.empty())
			throw EParseException(_Primitive, "if_bricks need at least one sbrick to test");
		ret = "if_bricks : ";
		for (uint i=0; i<_IfParams.size(); ++i)
		{
			ret += _IfParams[i];
			if (i < _IfParams.size()-1)
				ret += "; ";
		}
		ret += " : " + jumpToYes + NL;
		break;
	case it_sdb:
		if (_IfParams.empty())
			throw EParseException(_Primitive, "if_sdb need an expression to test");
		ret = "if_sdb : ";
		for (uint i=0; i<_IfParams.size(); ++i)
		{
			ret += _IfParams[i];
			if (i < _IfParams.size()-1)
				ret += " ";
		}
		ret += " : " + jumpToYes + NL;
		break;
	case it_race:
		if (_IfParams.empty())
			throw EParseException(_Primitive, "if_race need an expression to test");
		ret = "if_race : ";
		for (uint i=0; i<_IfParams.size(); ++i)
		{
			ret += _IfParams[i];
			if (i < _IfParams.size()-1)
				ret += " ";
		}
		ret += " : " + jumpToYes + NL;
		break;
	case it_cult:
		if (_IfParams.empty())
			throw EParseException(_Primitive, "if_cult need an expression to test");
		ret = "if_cult : ";
		for (uint i=0; i<_IfParams.size(); ++i)
		{
			ret += _IfParams[i];
			if (i < _IfParams.size()-1)
				ret += " ";
		}
		ret += " : " + jumpToYes + NL;
		break;
	case it_civ:
		if (_IfParams.empty())
			throw EParseException(_Primitive, "if_civ need an expression to test");
		ret = "if_civ : ";
		for (uint i=0; i<_IfParams.size(); ++i)
		{
			ret += _IfParams[i];
			if (i < _IfParams.size()-1)
				ret += " ";
		}
		ret += " : " + jumpToYes + NL;
		break;
	case it_faction_point:
		if (_IfParams.empty())
			throw EParseException(_Primitive, "if_faction_point need an expression to test");
		ret = "if_faction_point : ";
		for (uint i=0; i<_IfParams.size(); ++i)
		{
			ret += _IfParams[i];
			if (i < _IfParams.size()-1)
				ret += " ";
		}
		ret += " : " + jumpToYes + NL;
		break;
	case it_guild_civ:
		if (_IfParams.empty())
			throw EParseException(_Primitive, "it_guild_civ need an expression to test");
		ret = "if_guild_civ : ";
		for (uint i=0; i<_IfParams.size(); ++i)
		{
			ret += _IfParams[i];
			if (i < _IfParams.size()-1)
				ret += " ";
		}
		ret += " : " + jumpToYes + NL;
		break;
	case it_guild_cult:
		if (_IfParams.empty())
			throw EParseException(_Primitive, "it_guild_cult need an expression to test");
		ret = "if_guild_cult : ";
		for (uint i=0; i<_IfParams.size(); ++i)
		{
			ret += _IfParams[i];
			if (i < _IfParams.size()-1)
				ret += " ";
		}
		ret += " : " + jumpToYes + NL;
		break;
	case it_guild_fame:
		if (_IfParams.empty())
			throw EParseException(_Primitive, "it_guild_fame need an expression to test");
		ret = "if_guild_fame : ";
		for (uint i=0; i<_IfParams.size(); ++i)
		{
			ret += _IfParams[i];
			if (i < _IfParams.size()-1)
				ret += " ";
		}
		ret += " : " + jumpToYes + NL;
		break;
	case it_no_trial:
		ret = "if_no_trial : " + jumpToYes + NL;
		break;
	case it_item_in_inv:
		if (_IfParams.empty())
			throw EParseException(_Primitive, "if_item_in_inv need at least one mission to test");
		for (uint i=0; i<_IfParams.size(); ++i)
			ret += "if_item_in_inv : " + _IfParams[i] + " : " + jumpToYes + NL;
		break;
	}

	// Generate the 'no branch'

	if (!noSteps.empty())
	{
		ret += "# no: " + noSteps[0]->getStepName() + NL;
		for (uint i = 0; i < noSteps.size(); ++i)
		{
			if ( md.isThereAJumpTo(noSteps[i]->getStepName()) )
			{
				// insert a jump point
				ret += "jump_point : " + noSteps[i]->getStepName() + NL;
			}

			ret += noSteps[i]->genCode(md);
		}
	}

	// Generate the 'yes branch'

	if (!yesSteps.empty())
	{
		ret += "jump : " + getStepName() + "_endif" + NL;
		ret += "# yes: " + yesSteps[0]->getStepName() + NL;

		for (uint i = 0; i < yesSteps.size(); ++i)
		{
			if ((i == 0) || md.isThereAJumpTo(yesSteps[i]->getStepName()) )
			{
				// insert a jump point
				ret += "jump_point : " + yesSteps[i]->getStepName() + NL;
			}

			ret += yesSteps[i]->genCode(md);
		}
	}

	// Generate ending jump point

	ret += "jump_point : " + getStepName() + "_endif" + NL;

	return ret;
}

REGISTER_STEP_INDIRECT(CStepIf, "step_if");

// ////////////// //
// END OF STEP IF //
// ////////////// //

class CStepOOO : public CStepObjective
{
public:
	CStepOOO(CMissionData &md, IPrimitive *prim)
		: CStepObjective(md, prim)
	{
	}


	string genCode(CMissionData &md)
	{
		string ret;

		ret += CStepObjective::genCode(md);

		ret += IStep::genCodePreActions(md);

		ret += "ooo" + NL;

		string tmp = IStep::genCodeObjectives(md);
		tabulateLine(tmp, 1);
		ret += tmp;

		ret += "/ooo"+NL;

		ret += genCodePostActions(md);

		return ret;
	}

};
REGISTER_STEP_INDIRECT(CStepOOO, "step_ooo");

class CStepAny : public CStepObjective
{
public:
	CStepAny(CMissionData &md, IPrimitive *prim)
		: CStepObjective(md, prim)
	{
	}

	string genCode(CMissionData &md)
	{
		string ret;

		ret += CStepObjective::genCode(md);

		ret += IStep::genCodePreActions(md);

		ret += "any" + NL;

		string tmp = IStep::genCodeObjectives(md);
		tabulateLine(tmp, 1);

		ret += tmp;

		ret += "/any"+NL;

		ret += genCodePostActions(md);

		return ret;
	}

};
REGISTER_STEP_INDIRECT(CStepAny, "step_any");