#include "ScriptSystem.h"

#include "Utilities/Log.h"

// library includes
#include "physfs.h"

// system includes
#include <iostream>
#include <cmath>
#include <stdio.h>
#include <stdarg.h>

using namespace std;

namespace BlueCore
{
//--------------------------------------------------------------------------
static ScriptSystem* gScriptSystem = 0;

//--------------------------------------------------------------------------
void _sq_compiler_error_handler(HSQUIRRELVM vm, const SQChar *error,
		const SQChar *source, SQInteger line, SQInteger column)
{
	clog << "!!! Error compiling script '"<< source << ":"<< line << ": "
			<< error << endlog;
}

//--------------------------------------------------------------------------
static SQInteger _sq_runtime_error_handler(HSQUIRRELVM vm)
{
	const SQChar *error = 0;

	if (sq_gettop(vm) >= 1)
	{
		if (SQ_SUCCEEDED(sq_getstring(vm, 2, &error) ) )
		{
			clog << "!!! Script Error: "<< error << endlog;
		}
		else
		{
			clog << "!!! Script Error: unknown"<< endlog;
		}
	}

	return 0;
}

//--------------------------------------------------------------------------
void _sq_print(HSQUIRRELVM vm, const SQChar* s, ...)
{
	va_list vl;
	va_start ( vl, s );
	vprintf(s, vl);
	va_end ( vl );
}

//--------------------------------------------------------------------------
static SQInteger _sq_require(HSQUIRRELVM v)
{
	const SQChar *scriptname = 0;

	if (SQ_SUCCEEDED(sq_getstring(v, 2, &scriptname) ))
	{
		if (gScriptSystem)
			gScriptSystem->loadScript(scriptname);
	}

	return 0;
}

//--------------------------------------------------------------------------
SQInteger _sq_file_reader(SQUserPointer file)
{
	char c;
	unsigned int count;

	count = PHYSFS_read( (PHYSFS_file *)file, &c, sizeof (c), 1);
	if (count > 0)
		return c;

	return 0;
}

//--------------------------------------------------------------------------
ScriptSystem::ScriptSystem()
{
	gScriptSystem = this;

	_VM = sq_open( 1024);
	sq_setcompilererrorhandler(_VM, _sq_compiler_error_handler);
	sq_newclosure(_VM, _sq_runtime_error_handler, 0);
	sq_seterrorhandler(_VM);
	sq_setprintfunc(_VM, _sq_print);

	sq_pushroottable(_VM);
	sq_pushstring(_VM, "require", -1);
	sq_newclosure(_VM, _sq_require, 0);
	sq_setparamscheck(_VM, 2, ".s");
	sq_newslot(_VM, -3, false);
	sq_pop(_VM, 1);

	clog << ">>> ScriptSystem constructed ..."<< endlog;
}

//--------------------------------------------------------------------------
ScriptSystem::~ScriptSystem()
{
	sq_close(_VM);
	clog << ">>> ScriptSystem destructed ..."<< endlog;
}

//--------------------------------------------------------------------------
HSQUIRRELVM ScriptSystem::getVM()
{
	return _VM;
}

//--------------------------------------------------------------------------
bool ScriptSystem::loadScript(const std::string &name)
{
	for (unsigned int i = 0; i < _LoadedScripts.size(); i++)
	{
		if (_LoadedScripts[i] == name)
			return true;
	}

	if (executeScript(name) )
	{
		_LoadedScripts.push_back(name);
		return true;
	}
	else
		clog << "!!! Script '"<< name << "' not found!"<< endlog;

	return false;
}

//--------------------------------------------------------------------------
bool ScriptSystem::executeScript(const std::string &name)
{
	string filename = name + ".nut";
	PHYSFS_file *file = PHYSFS_openRead(filename.c_str() );

	if ( !file)
	{
		clog << "!!! Script '"<< name << "' not found!"<< endlog;
		return false;
	}

	SQInteger ret;

	ret = sq_compile(_VM, _sq_file_reader, file, name.c_str(), 1);
	PHYSFS_close(file);

	if (SQ_FAILED(ret) )
	{
		return false;
	}

	sq_pushroottable(_VM);

	ret = sq_call(_VM, 1, SQFalse, SQTrue);
	sq_pop(_VM, 1);

	if (SQ_FAILED(ret) )
	{
		clog << "!!! Script execution failed: "<< name << endlog;
		return false;
	}
	else
		return true;
}

//--------------------------------------------------------------------------
void ScriptSystem::callFunction(const std::string &name)
{
	sq_pushroottable(_VM);
	sq_pushstring(_VM, name.c_str(), -1);
	if (SQ_SUCCEEDED(sq_get(_VM, -2)) )
	{
		sq_pushroottable(_VM);
		if (sq_call(_VM, 1, SQTrue, SQTrue) )
			sq_pop(_VM, 1);
	}
	sq_pop(_VM, 1);
}

//--------------------------------------------------------------------------
void ScriptSystem::callFunction(const std::string &name, double value)
{
	sq_pushroottable(_VM);
	sq_pushstring(_VM, name.c_str(), -1);
	if (SQ_SUCCEEDED(sq_get(_VM, -2)) )
	{
		sq_pushroottable(_VM);
		sq_pushfloat(_VM, value);
		if (sq_call(_VM, 2, SQTrue, SQTrue) )
			sq_pop(_VM, 1);
	}
	sq_pop(_VM, 1);
}
}
