#include "Prefix.pch"
#include "LuaScript.h"
#include "CoreMath.h"
#include "Random.h"
#include "Simulation.h"
#include "Volume.h"
#include "QuadTree.h"
#include "ScriptStrategy.h"

/* 
This is the Lua script integration from the SIGGRAPH Poster, "Emergent Geometry".
Written for a C++03 compiler.
*/

using namespace std;
using namespace boost;

shared_ptr<Agent> LuaAgentFuncs::mCurrent;
bool LuaAgentFuncs::mPrintFunctionCalls = false;
bool LuaAgentFuncs::mAgentSurvives = true;

void LuaAgentFuncs::RegisterFuncsWithLua(lua_State* L)
{
	lua_register(L, "TurnTowardsVisibleAffectingVolumes", 
				 &LuaAgentFuncs::TurnTowardsVisibleAffectingVolumes);
	lua_register(L, "TurnLeft",
				 &LuaAgentFuncs::TurnLeft);
	lua_register(L, "TurnLeftRand",
				 &LuaAgentFuncs::TurnLeftRand);
	lua_register(L, "TurnRight",
				 &LuaAgentFuncs::TurnRight);
	lua_register(L, "TurnRightRand",
				 &LuaAgentFuncs::TurnRightRand);
	lua_register(L, "TurnUp",
				 &LuaAgentFuncs::TurnUp);
	lua_register(L, "TurnUpRand",
				 &LuaAgentFuncs::TurnUpRand);
	lua_register(L, "TurnDown",
				 &LuaAgentFuncs::TurnDown);
	lua_register(L, "TurnDownRand",
				 &LuaAgentFuncs::TurnDownRand);
	lua_register(L, "TurnTowardOrigin",
				 &LuaAgentFuncs::TurnTowardOrigin);
	lua_register(L, "TurnTowardOriginRand",
				 &LuaAgentFuncs::TurnTowardOriginRand);
	
	lua_register(L, "StepAgent",
				 &LuaAgentFuncs::StepAgent);
	lua_register(L, "DropVolume",
				 &LuaAgentFuncs::DropVolume);
	lua_register(L, "MoveAgentTo",
				 &LuaAgentFuncs::MoveAgentTo);
	lua_register(L, "MoveAgentToRandXZ",
				 &LuaAgentFuncs::MoveAgentToRandXZ);
	lua_register(L, "SetXDirection",
				 &LuaAgentFuncs::SetXDirection);
	lua_register(L, "SetYDirection",
				 &LuaAgentFuncs::SetYDirection);
	lua_register(L, "SetZDirection",
				 &LuaAgentFuncs::SetZDirection);
	
	lua_register(L, "SpawnAgent",
				 &LuaAgentFuncs::SpawnAgent);
	lua_register(L, "KillAgent",
				 &LuaAgentFuncs::KillAgent);
	
	lua_register(L, "Random",
				 &LuaAgentFuncs::Random);
	
	lua_register(L, "AvoidCollisions",
				 &LuaAgentFuncs::AvoidCollisions);
	lua_register(L, "SteerWithNeighbors",
				 &LuaAgentFuncs::SteerWithNeighbors);
	lua_register(L, "SteerAtNeighbors",
				 &LuaAgentFuncs::SteerAtNeighbors);
}

void LuaAgentFuncs::SetActiveAgent(uint Agent)
{
	mCurrent = Agent::GetAgent(Agent);
	mAgentSurvives = true;
}


int LuaAgentFuncs::TurnTowardOrigin(lua_State* L)
{
	float amount = lua_tonumber(L, -1);
	lua_pop(L, 1);
	
	if( __builtin_expect(amount < 0.0f || amount > 1.0f, false) )
	{
		Simulation::Instance()->LogText("Warning: In function TurnTowardOriginRand, amount out of range [0, 1], setting value to 0.5");
		amount = 0.5f;
	}
	
	// I would use SLERP here if it were implemented already
	float3 direction = mCurrent->GetDirection(); 
	float3 originDir = Math::QuickNorm(mCurrent->GetPosition() * -1.0f);
	mCurrent->SetDirection( Math::QuickNorm(direction + (originDir * amount)) );
	
	return 0;
}

int LuaAgentFuncs::TurnTowardOriginRand(lua_State* L)
{
	float min = lua_tonumber(L, -2);
	float max = lua_tonumber(L, -1);
	lua_pop(L, 2);
	
	if( __builtin_expect(min < 0.0f, false) )
	{
		Simulation::Instance()->LogText("Warning: In function TurnTowardOriginRand, minimum out of range, setting value to 0");
		min = 0.0f;
	}
	if( __builtin_expect(max > 1.0f, false) )
	{
		Simulation::Instance()->LogText("Warning: In function TurnTowardOriginRand, maximum out of range, setting value to 1");
		max = 1.0f;
	}
	if( __builtin_expect(min >= max, false) )
	{
		Simulation::Instance()->LogText("Warning: In function TurnTowardOriginRand, maximum is less than minumum.");
		max = min;
	}
	
	// I would use SLERP here if it were implemented already
	float amount = Random::Instance()->GetValue(min, max);
	float3 direction = mCurrent->GetDirection(); 
	float3 originDir = Math::QuickNorm(mCurrent->GetPosition() * -1.0f);
	mCurrent->SetDirection( Math::QuickNorm(direction + (originDir * amount)) );
	
	return 0;
}

int LuaAgentFuncs::AvoidCollisions(lua_State* L)
{
	float r = lua_tonumber(L, -1);
	lua_pop(L, 1);
	
	float3 dir(0.0f);
	for( uint i = 0; i < Agent::GetNumAgents(); ++i )
	{
		if( mCurrent != Agent::GetAgent(i) &&
			Math::Mag(mCurrent->GetPosition(), Agent::GetAgent(i)->GetPosition()) <= r )
		{
			// Direction of the possible collision
			dir += Math::QuickNorm(Agent::GetAgent(i)->GetPosition() - mCurrent->GetPosition());
		}
	}
	dir = Math::QuickNorm(dir);
	
	mCurrent->SetDirection( (dir + mCurrent->GetDirection()) * 0.5f );
	
	return 0;
}

int LuaAgentFuncs::SteerWithNeighbors(lua_State* L)
{
	float radius = lua_tonumber(L, -1);
	lua_pop(L, 1);
	
	float3 Dir = mCurrent->GetDirection();
	
	for( uint i = 0; i < Agent::GetNumAgents(); ++i )
	{
		if( mCurrent != Agent::GetAgent(i) &&
			Math::Mag(mCurrent->GetPosition(), Agent::GetAgent(i)->GetPosition()) <= radius )
		{
			Dir += Agent::GetAgent(i)->GetDirection();
		}
	}
	
	mCurrent->SetDirection((Math::QuickNorm(Dir)+mCurrent->GetDirection()) * 0.5f);
	
	return 0;
}

int LuaAgentFuncs::SteerAtNeighbors(lua_State* L)
{
	float radius = lua_tonumber(L, -1);
	lua_pop(L, 1);
	
	float3 Dir = mCurrent->GetDirection();
	
	for( uint i = 0; i < Agent::GetNumAgents(); ++i )
	{
		if( mCurrent != Agent::GetAgent(i) &&
			Math::Mag(mCurrent->GetPosition(), Agent::GetAgent(i)->GetPosition()) <= radius )
		{
			Dir += Agent::GetAgent(i)->GetPosition();
		}
	}
	
	mCurrent->SetDirection((Math::QuickNorm(Dir) + mCurrent->GetDirection()) * 0.5f);
	
	return 0;
}

int LuaAgentFuncs::KillAgent(lua_State* L)
{
	Agent::KillAgent(mCurrent);
	mAgentSurvives = false;
	return 0;
}

int LuaAgentFuncs::Random(lua_State* L)
{
	float r = Random::Instance()->GetValue(static_cast<float>(lua_tonumber(L, -2)),
										   static_cast<float>(lua_tonumber(L, -1)));
	lua_pop(L, 2);
	lua_pushnumber(L, r);
	return 1;
}

int LuaAgentFuncs::SpawnAgent(lua_State* L)
{
	Agent::NewAgent(mCurrent->GetPosition(), 
					Random::Instance()->GetDirection(),
					mCurrent->GetColor());
					//Random::Instance()->GetColor());
	return 0;
}

int LuaAgentFuncs::SetXDirection(lua_State* L)
{
	mCurrent->SetXDir(lua_tonumber(L, -1));
	lua_pop(L, 1);
	return 0;
}

int LuaAgentFuncs::SetYDirection(lua_State* L)
{
	mCurrent->SetYDir(lua_tonumber(L, -1));
	lua_pop(L, 1);
	return 0;
}

int LuaAgentFuncs::SetZDirection(lua_State* L)
{
	mCurrent->SetZDir(lua_tonumber(L, -1));
	lua_pop(L, 1);
	return 0;
}

int LuaAgentFuncs::MoveAgentToRandXZ(lua_State* L)
{
	float3 simSize = Simulation::Instance()->GetSize();
	simSize *= 0.5f;
	float3 pos(Random::Instance()->GetValue(-simSize.X(), simSize.X()),
			   mCurrent->GetPosition().Y(),
			   Random::Instance()->GetValue(-simSize.Z(), simSize.Z()));
			   
	mCurrent->SetPosition(pos);
	
	return 0;
}
int LuaAgentFuncs::MoveAgentTo(lua_State* L)
{	
	float3 pos(lua_tonumber(L, -3), lua_tonumber(L, -2), lua_tonumber(L, -1));
	
	if( __builtin_expect( mPrintFunctionCalls, false ) )
	{
		cout << __FUNCTION__ << " called.\n";
		cout << "\tMoving agent to: " << pos << endl;
	}
	
	mCurrent->SetPosition(pos);
	lua_pop(L, 3);
	
	return 0;
}

int LuaAgentFuncs::TurnTowardsVisibleAffectingVolumes(lua_State* L)
{
	Assert(L, string("Invalid lua_State passed to ") + __FUNCTION__);
	Assert(mCurrent.get(), "Cannot call a lua func without a valid agent.");

	float turnPercent = lua_tonumber(L, -1);
	lua_pop(L, 1);
	
	if( __builtin_expect( mPrintFunctionCalls, false ) )
	{
		cout << __FUNCTION__ << " called.\n";
		cout << "\tArgument 1: " << turnPercent << endl;
	}
	
	float3 dir = mCurrent->GetDirection();
	float3 pos = mCurrent->GetPosition();
	
	vector<shared_ptr<Volume> > volumes = Simulation::Instance()->GetTree()->GetInfluences(pos);
	float3 infl(0.0f);
	
	for( vector<shared_ptr<Volume> >::iterator i = volumes.begin();
		 i != volumes.end(); ++i )
	{
		shared_ptr<Volume> vol = *i;
		//if( mCurrent->IsVisible(vol->GetPosition()) )
		{
			float3 volDir = Math::QuickNorm(vol->GetPosition() - pos);
			float weight = vol->GetWeight(pos);
			infl += volDir * weight;
		}
	}
	infl = Math::QuickNorm(infl);
	
	mCurrent->SetDirection( infl );
	
	return 0;
}
int LuaAgentFuncs::StepAgent(lua_State* L)
{
	Assert(L, string("Invalid lua_State passed to ") + __FUNCTION__);
	Assert(mCurrent.get(), "Cannot call a lua func without a valid agent.");
	if( __builtin_expect( mPrintFunctionCalls, false ) ) cout << __FUNCTION__ << " called." << endl;
	
	mCurrent->Step();
	
	return 0;
}
int LuaAgentFuncs::DropVolume(lua_State* L)
{
	Assert(L, string("Invalid lua_State passed to ") + __FUNCTION__);
	Assert(mCurrent.get(), "Cannot call a lua func without a valid agent.");
	
	float radius = lua_tonumber(L, -1);
	lua_pop(L, 1);
	
	if( __builtin_expect( mPrintFunctionCalls, false ) )
	{
		cout << __FUNCTION__ << " called.\n";
		cout << "\tArgument 1: " << radius << endl;
	}
	
	Simulation::Instance()->GetTree()->Add(shared_ptr<Volume>(new Volume(mCurrent->GetPosition(), mCurrent->GetColor(), radius)));
	return 0;
}

int LuaAgentFuncs::TurnLeft(lua_State* L)
{
	Assert(L, string("Invalid lua_State passed to ") + __FUNCTION__);
	Assert(mCurrent.get(), "Cannot call a lua func without a valid agent.");
	if( __builtin_expect( mPrintFunctionCalls, false ) ) cout << __FUNCTION__ << " called." << endl;
	
	float angle = lua_tonumber(L, -1);
	lua_pop(L, 1);
	
	float3 dir = mCurrent->GetDirection();
	mCurrent->SetDirection( Math::Transform(float4x4::MakeYRotation(Math::RadPerDeg * angle), dir) );
	
	return 0;
}
int LuaAgentFuncs::TurnLeftRand(lua_State* L)
{
	Assert(L, string("Invalid lua_State passed to ") + __FUNCTION__);
	Assert(mCurrent.get(), "Cannot call a lua func without a valid agent.");
	
	float min = lua_tonumber(L, -2);
	float max = lua_tonumber(L, -1);
	lua_pop(L, 2);
	
	if( __builtin_expect( mPrintFunctionCalls, false ) )
	{
		cout << __FUNCTION__ << " called.\n";
		cout << "\tArgument 1: " << min << 
				"\n\tArgument 2: " << max << endl;
	}
	
	float angle = Random::Instance()->GetValue(min, max);
	
	float3 dir = mCurrent->GetDirection();
	mCurrent->SetDirection( Math::Transform(float4x4::MakeYRotation(Math::RadPerDeg * angle), dir) );
	
	return 0;
}
int LuaAgentFuncs::TurnRight(lua_State* L)
{
	Assert(L, string("Invalid lua_State passed to ") + __FUNCTION__);
	Assert(mCurrent.get(), "Cannot call a lua func without a valid agent.");
	if( __builtin_expect( mPrintFunctionCalls, false ) ) cout << __FUNCTION__ << " called." << endl;
	
	float angle = lua_tonumber(L, -1);
	lua_pop(L, 1);
	
	float3 dir = mCurrent->GetDirection();
	mCurrent->SetDirection( Math::Transform(float4x4::MakeYRotation(-1.0f * Math::RadPerDeg * angle), dir) );
	
	return 0;
}	
int LuaAgentFuncs::TurnRightRand(lua_State* L)
{
	Assert(L, string("Invalid lua_State passed to ") + __FUNCTION__);
	Assert(mCurrent.get(), "Cannot call a lua func without a valid agent.");
	
	float min = lua_tonumber(L, -2);
	float max = lua_tonumber(L, -1);
	lua_pop(L, 2);
	
	if( __builtin_expect( mPrintFunctionCalls, false ) )
	{
		cout << __FUNCTION__ << " called.\n";
		cout << "\tArgument 1: " << min << 
			"\n\tArgument 2: " << max << endl;
	}
	
	float angle = Random::Instance()->GetValue(min, max);
	
	float3 dir = mCurrent->GetDirection();
	mCurrent->SetDirection( Math::Transform(float4x4::MakeYRotation(-1.0f * Math::RadPerDeg * angle), dir) );
	
	return 0;
}
int LuaAgentFuncs::TurnUp(lua_State* L)
{
	Assert(L, string("Invalid lua_State passed to ") + __FUNCTION__);
	Assert(mCurrent.get(), "Cannot call a lua func without a valid agent.");
	if( __builtin_expect( mPrintFunctionCalls, false ) ) cout << __FUNCTION__ << " called." << endl;
	
	float angle = lua_tonumber(L, -1);
	lua_pop(L, 1);
	
	float3 dir = mCurrent->GetDirection();
	float3 axis = Math::QuickNorm(Math::Cross(float3(0.0f, 1.0f, 0.0f), dir));
	
	mCurrent->SetDirection( Math::Transform(
				float4x4::MakeAxisRotation(axis, Math::RadPerDeg * angle), dir) );
	
	return 0;
}

int LuaAgentFuncs::TurnUpRand(lua_State* L)
{
	Assert(L, string("Invalid lua_State passed to ") + __FUNCTION__);
	Assert(mCurrent.get(), "Cannot call a lua func without a valid agent.");
	
	float min = lua_tonumber(L, -2);
	float max = lua_tonumber(L, -1);
	lua_pop(L, 2);
	
	if( __builtin_expect( mPrintFunctionCalls, false ) )
	{
		cout << __FUNCTION__ << " called.\n";
		cout << "\tArgument 1: " << min << 
			"\n\tArgument 2: " << max << endl;
	}
	
	float angle = Random::Instance()->GetValue(min, max);
	float3 dir = mCurrent->GetDirection();
	float3 axis = Math::QuickNorm(Math::Cross(float3(0.0f, 1.0f, 0.0f), dir));
	
	mCurrent->SetDirection( Math::Transform(
				float4x4::MakeAxisRotation(axis, Math::RadPerDeg * angle), dir) );
	return 0;
}
int LuaAgentFuncs::TurnDown(lua_State* L)
{
	Assert(L, string("Invalid lua_State passed to ") + __FUNCTION__);
	Assert(mCurrent.get(), "Cannot call a lua func without a valid agent.");
	if( __builtin_expect( mPrintFunctionCalls, false ) ) cout << __FUNCTION__ << " called." << endl;
	
	float angle = lua_tonumber(L, -1);
	lua_pop(L, 1);
	
	float3 dir = mCurrent->GetDirection();
	float3 axis = Math::QuickNorm(Math::Cross(float3(0.0f, 1.0f, 0.0f), dir));
	
	mCurrent->SetDirection( Math::Transform(
							float4x4::MakeAxisRotation(axis, -1.0f * Math::RadPerDeg * angle), dir) );
	
	return 0;
}
int LuaAgentFuncs::TurnDownRand(lua_State* L)
{	
	Assert(L, string("Invalid lua_State passed to ") + __FUNCTION__);
	Assert(mCurrent.get(), "Cannot call a lua func without a valid agent.");
	
	float min = lua_tonumber(L, -2);
	float max = lua_tonumber(L, -1);
	lua_pop(L, 2);
	
	if( __builtin_expect( mPrintFunctionCalls, false ) )
	{
		cout << __FUNCTION__ << " called.\n";
		cout << "\tArgument 1: " << min << 
			"\n\tArgument 2: " << max << endl;
	}
	
	float angle = Random::Instance()->GetValue(min, max);
	float3 dir = mCurrent->GetDirection();
	float3 axis = Math::QuickNorm(Math::Cross(float3(0.0f, 1.0f, 0.0f), dir));
	
	mCurrent->SetDirection( Math::Transform(
					float4x4::MakeAxisRotation(axis, -1.0f * Math::RadPerDeg * angle), dir) );
	return 0;
}

// LuaEnvFuncs Implementation //
boost::shared_ptr<QuadTree> LuaEnvFuncs::mEnvironment;
bool LuaEnvFuncs::mPrintFunctionCalls = false;
void LuaEnvFuncs::RegisterFuncsWithLua(lua_State* L)
{	
	lua_register(L, "Evaporate", 
				 &LuaEnvFuncs::Evaporate);
}

int LuaEnvFuncs::Evaporate(lua_State* L)
{
	Assert(L, string("Invalid lua_State passed to ") + __FUNCTION__);
	Assert(mEnvironment.get(), "Cannot call a lua environment func without a valid QuadTree.");
	if( __builtin_expect( mPrintFunctionCalls, false ) ) cout << __FUNCTION__ << " called." << endl;
	
	float percent = lua_tonumber(L, -1);
	lua_pop(L, 1);
	
	mEnvironment->Evaporate(percent);
	
	return 0;
}


// Agent Implementation //
vector<shared_ptr<Agent> > Agent::mAllAgents;
shared_ptr<Agent> Agent::NewAgent(const float3& Position, 
							const float3& Direction,
							const float3& Color)
{
	shared_ptr<Agent> a(new Agent(Position, Direction, mAllAgents.size(), Color));
	mAllAgents.push_back(a);
	return a;
}


void Agent::KillAgent(shared_ptr<Agent> A)
{
	vector<shared_ptr<Agent> >::iterator i = find(mAllAgents.begin(), mAllAgents.end(), A);
	if( i != mAllAgents.end() )
	{
		mAllAgents.erase(i);
	}
}

void Agent::Reset()
{
	mAllAgents.clear();
}

shared_ptr<Agent> Agent::GetAgent(uint Index)
{
	return mAllAgents[Index];
}

Agent::Agent(const float3& Position,
			 const float3& Direction,
			 uint ID,
			 const float3& Color)
{
	mPosition = Position;
	mDirection = Direction;
	mColor = Color;
	mID = ID;
	mSpeed = 1.0f;
	
	mFOV = 0.25f;
}

void Agent::Draw() const
{
	glDisable(GL_LIGHTING);
	glColor3f(0.0f, 0.0f, 0.0f);
	
	// Draw a point at the agent
	glPointSize(8.0f);
	glBegin(GL_POINTS);
	glVertex3fv(mPosition.Get());
	glEnd();
	
	// Draw the direction vector as a line
	glBegin(GL_LINES);
	glVertex3fv(mPosition.Get());
	glVertex3fv(float3(mPosition + (mDirection*3.0f)).Get());
	glEnd();
	
	glEnable(GL_LIGHTING);
}

bool Agent::IsVisible(const float3& Point)
{	
	float3 dir = Math::QuickNorm(Point - mPosition);
	float d = dot(dir, mDirection);
	
	return (d > mFOV);
}

void Agent::Step()
{
	mDirection = Math::QuickNorm(mDirection);
	
	if ( true )
	{
		const float3& simsize = Simulation::Instance()->GetSize() * 0.5f;
		float3 newPos = mPosition;
		newPos += mDirection * mSpeed;
		
		if( fabs(newPos.X()) >= simsize.X() )
		{
			mDirection[0] *= -1.0f;
		}
		
		if( fabs(newPos.Y()) >= simsize.Y() )
		{
			mDirection[1] *= -1.0f;
		}
		
		if( fabs(newPos.Z()) >= simsize.Z() )
		{
			mDirection[2] *= -1.0f;
		}
	}
	
	mPosition += mDirection * Random::Instance()->GetValue(0.0f, mSpeed*2.0f);
}

void LuaScript::CheckScriptStatus(int status, const string& Filename)
{
	// Do error checking for the loaded and preprocessed script file
	if( status != 0 && !mReportedError )
	{
		ostringstream error;
		switch(status)
		{
			case LUA_ERRFILE:
				error << "Cannot open/read file: " << Filename << "\n";
				break;
			case LUA_ERRSYNTAX:
				error << "Cannot run script: '" << Filename
				<< "' because of syntax error(s):" << "\n";
				error << lua_tostring(mState, -1) << "\n";
				break;
			case LUA_ERRMEM:
				error << "Cannot run script: '" << Filename
				<< "' because of memory allocation error(s)." << "\n";
				error << lua_tostring(mState, -1) << "\n";
				break;
			default:
				error << "Cannot run script: '" << Filename
				<< "', unknown error." << "\n";
				error << lua_tostring(mState, -1) << "\n";
		}
		Simulation::Instance()->LogText(error.str());
		mReportedError = true;
	}
}

LuaScript::LuaScript(const string& BehaviorName)
{
	Assert(BehaviorName.size() > 0, "Cannot create a script behavior without a name.");
	mName = BehaviorName;
	
	mState = lua_open();
	
	mReportedError = false;
	
	RegisterFunctions();
	luaL_openlibs(mState);
	
	CheckScriptStatus(luaL_loadfile( mState, BehaviorName.c_str() ), BehaviorName);
	
	lua_pcall(mState, 0, 0, 0);
	lua_getglobal(mState, "number_of_agents");
	lua_getglobal(mState, "initial_position");
	lua_getglobal(mState, "size_x");
	lua_getglobal(mState, "size_y");
	lua_getglobal(mState, "size_z");
	lua_getglobal(mState, "min_vol_radius");
	
	mNum = static_cast<int>(lua_tointeger(mState, -6));
	mDistribution = string(lua_tostring(mState, -5));
	mSize = float3( static_cast<float>(lua_tonumber(mState, -4)),
					static_cast<float>(lua_tonumber(mState, -3)),
					static_cast<float>(lua_tonumber(mState, -2)) );
	mRadiusThresh = static_cast<float>(lua_tonumber(mState, -1));
	lua_pop(mState, 5);
}

LuaScript::~LuaScript()
{
	lua_close(mState);
}

void LuaScript::RegisterFunctions()
{
	Assert(mState, "Cannot register the c functions with lua before the lua state is initialized.");
	LuaAgentFuncs::RegisterFuncsWithLua(mState);
	LuaEnvFuncs::RegisterFuncsWithLua(mState);
}

void LuaScript::SetAgent(uint Index)
{
	mAgent = Index;
}

void LuaScript::UpdateEnvironment(shared_ptr<QuadTree>& Environment)
{
	lua_getfield(mState, LUA_GLOBALSINDEX, "UpdateEnvironment");
	
	LuaEnvFuncs::SetEnvironment(Environment);
	
	// Create a table with all the volumes in the scene in it
	lua_newtable(mState);
	list<shared_ptr<Volume> > Volumes = Environment->GetAllVolumes();
	int depth = 0;
	
	for( list<shared_ptr<Volume> >::iterator i = Volumes.begin();
		 i != Volumes.end(); ++i )
	{
		// Radius
		lua_pushnumber(mState, depth++);
		lua_pushnumber(mState, (*i)->GetRadius());
		lua_rawset(mState, -3);
		
		// X Y Z
		lua_pushnumber(mState, depth++);
		lua_pushnumber(mState, (*i)->GetPosition().X());
		lua_rawset(mState, -3);
		
		lua_pushnumber(mState, depth++);
		lua_pushnumber(mState, (*i)->GetPosition().Y());
		lua_rawset(mState, -3);
		
		lua_pushnumber(mState, depth++);
		lua_pushnumber(mState, (*i)->GetPosition().Z());
		lua_rawset(mState, -3);
	}
	
	// Run the script
	int state = lua_pcall(mState, 1, LUA_MULTRET, 0);
	CheckScriptSucessful(state);
	
	lua_settop(mState, 0);	// Clear the stack
}

void LuaScript::Run()
{
	Assert( mState, "Run called with an invalid lua_State*");
	Assert( mAgent <= Agent::GetNumAgents(), "Invalid agent index.");
	
	shared_ptr<Agent> a = Agent::GetAgent(mAgent);
	std::vector<boost::shared_ptr<Volume> > Volumes = 
		Simulation::Instance()->GetTree()->GetInfluences( a->GetPosition() );
	
	lua_getfield(mState, LUA_GLOBALSINDEX, "UpdateAgent");
	
	// Create a table to store the agent and volumes
	lua_newtable(mState);	// Agent data
	
	// Put the agent in the stack
	lua_pushnumber(mState, 1);
	lua_pushnumber(mState, a->GetPosition().X());
	lua_rawset(mState, -3);
	
	lua_pushnumber(mState, 2);
	lua_pushnumber(mState, a->GetPosition().Y());
	lua_rawset(mState, -3);
	
	lua_pushnumber(mState, 3);
	lua_pushnumber(mState, a->GetPosition().Z());
	lua_rawset(mState, -3);
	
	lua_pushnumber(mState, 4);
	lua_pushnumber(mState, a->GetDirection().X());
	lua_rawset(mState, -3);
	
	lua_pushnumber(mState, 5);
	lua_pushnumber(mState, a->GetDirection().Y());
	lua_rawset(mState, -3);
	
	lua_pushnumber(mState, 6);
	lua_pushnumber(mState, a->GetDirection().Z());
	lua_rawset(mState, -3);
	
	lua_pushnumber(mState, Volumes.size());
	
	LuaAgentFuncs::SetActiveAgent(mAgent);
	
	// Run the script
	int state = lua_pcall(mState, 2, LUA_MULTRET, 0);
	CheckScriptSucessful( state );
	
	lua_settop(mState, 0);	// Clear the stack
}

void LuaScript::CheckScriptSucessful(int state)
{
	if( state != 0 && !mReportedError )
	{
		ostringstream err;
		switch( state )
		{
			case LUA_ERRRUN:
				err << "Script runtime error in behavior: " << mName << "\n";
				err << lua_tostring(mState, -1) << "\n";
				break;
			case LUA_ERRMEM:
				err << "Memory allocation error in script for behavior: " << mName << "\n";
				err << lua_tostring(mState, -1) << "\n";
				break;
			case LUA_ERRERR:
				err << "Error in the error handler (multiple errors) in a script for behavior: " 
				<< mName << "\n";
				err << lua_tostring(mState, -1) << "\n";
				break;
			default:
				err << "Unknown error in script for behavior: " << mName << "\n";
				err << lua_tostring(mState, -1) << "\n";
		}
		Simulation::Instance()->LogText(err.str());
		mReportedError = true;
	}
}