//-------------------------------------------------------------------------------------------------
// <copyright file="LWInvokeLuaInternalCmd.cpp" company="LiquidRobotics">
//  Copyright (c) Liquid Robotics Corporation, confidential and proprietary.  All rights reserved. 
// </copyright>
//
// <summary>
//	Implementation of LWInvokeLuaInternalCmd, a delegate class that carries a script
//	method invocation is queued up on the lua instance specific thread.  When it runs,
//	invokes the appropriate script method.
// </summary>
//
// <owner>Mike Cookson</owner>
//-------------------------------------------------------------------------------------------------

#include "LWAsynchScriptContextLua.h"
#include "LWController.h"
#include "LWInvokeLuaInternalCmd.h"
#include "LqrSMACLuaInstance.h"
#include "LqrSMACLogger.h"
#include <boost/algorithm/string.hpp>

using namespace std;
using namespace LiquidRobotics::SMACFramework;

namespace LiquidRobotics {
namespace LuaWrapper {

LWInvokeLuaInternalCmd::LWInvokeLuaInternalCmd(TCon& _ctrl, const string& _cmd, const string& _instanceName,
		const string& _functionName, const StringArray& _args, LWAsynchScriptContextLua* _pContext)
: LWConcreteCmd(_ctrl)
, ctrl(_ctrl)
, cmd(_cmd)
, instanceName(_instanceName)
, functionName(_functionName)
, args(_args.begin(), _args.end())
, pContext(_pContext)
, contextInstalled(false)
{
}

LWInvokeLuaInternalCmd::~LWInvokeLuaInternalCmd()
{
	if (!contextInstalled && pContext)
		delete pContext;
}

// this method is called by the background thread to execute the command
void LWInvokeLuaInternalCmd::Execute()
{
	typedef boost::shared_ptr<LuaInstance> LuaInstancePtr;

	try
	{
		SignalStarted();

		LuaInstancePtr inst = ctrl.GetLuaInstance(instanceName);
		SetContext(*inst);
		cmdResponse = RunCommand(*inst);
		SetException(TEx::NoException, "");
	}
	catch(exception& ex)
	{
		const int exNum = TEx::ExceptionToNumber(ex);
		const string exMsg = ex.what();

		SetException(exNum, exMsg);
	}
	catch(...)
	{
		TEx::NotReady exc("");
		const int exNum = TEx::ExceptionToNumber(exc);

		SetException(exNum, "caught unknown exception");
	}
}

string LWInvokeLuaInternalCmd::RunCommand(LuaInstance& L)
{
	const LuaInstance::StackStateHolder stateHolder(L);
	stringstream msg;

	L.getglobal(functionName.c_str());

	if (!L.isfunction(-1))
	{
		msg << functionName << " is not a function/procedure in instance " << instanceName;
		throw invalid_argument(msg.str());
	}

	for (StringArray::const_iterator ci = args.begin(); ci != args.end(); ++ci)
		L.pushlstring(*ci);

	L.pcall(args.size(), LUA_MULTRET, 0, true);

	const int numResults = L.gettop()-stateHolder.top;

	if (formatter)
	{
		for (int i = 0; i < numResults; ++i)
		{
			LuaInstance::StringPtr res = L.tolstring(i-numResults);
			string res2 = (res ? *res : "nil");

			formatter->AppendResult(res2);
		}
	}

	for (int i = 0; i < numResults; ++i)
	{
		LuaInstance::StringPtr res = L.tolstring(i-numResults);

		if (i)
			msg << "/";

		msg << (res ? *res : "nil");
	}

	return msg.str();
}

void LWInvokeLuaInternalCmd::SetContext(LuaInstance& L)
{
	if (pContext)
	{
		//const LuaInstance::StackStateHolder stateHolder(L);

		Luna2<LWAsynchScriptContextLua>::Constructor(L, pContext);
		L.setglobal("SMCContext");
		L.gc(LUA_GCCOLLECT, 0);					// clean up the previous context, which has been replaced
		contextInstalled = true;
	}
}

// get the output of the command
string LWInvokeLuaInternalCmd::GetResult()
{
	DoThrow();
	return cmdResponse;
}


}
}
