/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "core/ace.h"
		#include "ascii/parser.h"
		#include "log/log.h"

		using namespace GS::ACE;


//------------------------------------------------------------------------------
void				Manager::UpdateACEUnit(Unit *u, float dt)
{
	if	(!u->pc)
		u->is_done = true;
	if	(u->is_done)
		return;

	// Execute sequence until next exec opcode.
	bool has_exec_left = false, looped = false;

	List <Command> ::Item *cpc;
	for (cpc = u->pc; cpc; cpc = cpc->Next())
	{
		Command *cmd = &cpc->Object();
		if	(cmd->code == AceCommandExec)
		{
			cpc = cpc->Next();
			break;
		}

		if	(cmd->duration_left > 0.00001)
		{
			float exec_dt = cmd->duration_left;
			if	(dt < cmd->duration_left)
			{
				exec_dt = dt;
				has_exec_left = true;
			}

			u->ExecCommand(cmd, exec_dt);
			cmd->duration_left -= exec_dt;

			if	(cmd->code == AceCommandNext)
				looped = true;
		}
	}

	// Sequence done.
	if	(looped)
	{
		// We need to reset the looping block commands duration.
		for (List <Command> ::Item *lpc = u->loop_pc; lpc != cpc; lpc = lpc->Next())
			lpc->Object().duration_left = lpc->Object().duration;
	}
	else
		if	(!has_exec_left)
			u->pc = cpc;
}
//------------------------------------------------------------------------------

static bool DefineNameCompare(const Define *o, const GS::String &name) { return o->id == name; }

//------------------------------------------------------------------------------
int					Manager::LoadACECommandList(const char *s, Unit *u) const
{
	if	(!s)
		__ERR__(__LOG_E__ << "Cannot load a null command list.\n", -1);

	u->ResetCommandList();

	using namespace	AsciiParser;

	// Parse command list.
	const char *e = s + String::strlen(s);
	s = SkipSpace(s, e);

	while (s < e)
	{
		Command &cc = u->list.Add(Command())->Object();

		// Read in code.
		String command(s, SkipEntry(s, e));

		Define *d = ArrayListFindEx(define_list, DefineNameCompare, command);
		if	(!d)
			__ERR__(__LOG_E__ << "Undefined ACE command '" << command << "'.\n", -1);
		cc.code = d->code;

		// Read in duration.
		s = NextEntry(s, e);
		if	(s == e)
			__ERR__(__LOG_E__ << "No duration for ACE command '" << command << "'.\n", -1);

		cc.duration = String(s, SkipEntry(s, e)).Float();
		if	(cc.duration < 0.001f)
			cc.duration = 0.001f;		// Force minimum duration to 1ms.
		cc.duration_left = cc.duration;

		// Read up to 3 parameters.
		s = NextEntry(s, e);
		uint nparm = 0;

		while (s < e)
		{
			if	((s[0] == '+') || (s[0] == ';'))
				break;

			if	(nparm == 3)
				__ERR__(__LOG_E__ << "ACE compiled with " << max_command_param << " command parameter(s) maximum.\n", -1);
			if	(s[0] != ',')
				__ERR__(__LOG_E__ << "Expected ',' in ACE list declaration.\n", -1);
			s = SkipSpace(s + 1, e);
			if	(s == e)
				__ERR__(__LOG_E__ << "Expected ACE command parameter following ','.\n", -1);

			String parm(s, SkipEntry(s, e, true));
			cc.parm[nparm++] = parm.Float();
			s = SkipSpace(SkipEntry(s, e, true), e);
		}
		if	(s == e)
			__ERR__(__LOG_E__ << "Mangled ACE command list.\n", -1);
		if	((uint)d->nparm != nparm)
			__ERR__(__LOG_E__ << "ACE command '" << command << "' takes " << d->nparm << " parameter(s), found " << nparm << ".\n", -1);

		// Output exec command.
		if	(s[0] == ';')
			u->list.Add(Command())->Object().code = AceCommandExec;

		s = SkipSpace(s + 1, e);	// Skip comma.
	}

	// Set PC to list root command.
	u->is_done = false;
	u->pc = u->list.GetRoot();
	return u->list.GetCount();
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Manager::DefineACECommand(const char *command, int code, int nparm, bool user_code)
{
	if	(user_code && (code < 0))
		__ERR__(__LOG_E__  << "Negative command codes are reserved.\n", false)

	Define *d = new Define;
	if	(!d)
		__ERR__(__LOG_E__ << "Failed to allocate new container to define ACE command '" << command << "'.\n", false)

	d->id = command;
	d->code = code;
	d->nparm = nparm;

	define_list.Add(d);
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
Manager::Manager()
{
	DefineACECommand("nop", AceCommandNop, 0, false);
	DefineACECommand("loop", AceCommandLoop, 0, false);
	DefineACECommand("next", AceCommandNext, 0, false);
}
Manager::~Manager()
{
	ArrayListDeleteAllPtr(Define *, define_list)
}
//------------------------------------------------------------------------------
