#include "squirrel.h"
#include "squirrel_object.h"
//#include "SquirrelVM.h"

SquirrelObject::SquirrelObject(HSQUIRRELVM v)
{
	vm = v;
	 sq_resetobject(&_o);
}

SquirrelObject::~SquirrelObject()
{
	if(vm)
		sq_release(vm,&_o);
}

SquirrelObject::SquirrelObject(const SquirrelObject &o)
{
	vm = o.vm;
	_o = o._o;
	sq_addref(vm,&_o);
}

SquirrelObject::SquirrelObject(HSQOBJECT &o)
{
	_o = o;
	sq_addref(vm,&_o);
}

SquirrelObject SquirrelObject::Clone()
{
	SquirrelObject ret(vm);
	if(GetType() == OT_TABLE || GetType() == OT_ARRAY)
	{
		sq_pushobject(vm,_o);
		sq_clone(vm,-1);
		ret.AttachToStackObject(-1);
		sq_pop(vm,2);
	}
	return ret;
}

SquirrelObject & SquirrelObject::operator =(const SquirrelObject &o)
{
	HSQOBJECT t;
	t = o._o;
	sq_addref(vm,&t);
	sq_release(vm,&_o);
	_o = t;
	return *this;
}

SquirrelObject & SquirrelObject::operator =(int n)
{
	sq_pushinteger(vm,n);
	AttachToStackObject(-1);
	sq_pop(vm,1);
	return *this;
}

void SquirrelObject::Append(const SquirrelObject &o)
{
	if(sq_isarray(_o)) {
		sq_pushobject(vm,_o);
		sq_pushobject(vm,o._o);
		sq_arrayappend(vm,-2);
		sq_pop(vm,1);
	}
}

void SquirrelObject::AttachToStackObject(int idx)
{
	HSQOBJECT t;
	sq_getstackobj(vm,idx,&t);
	sq_addref(vm,&t);
	sq_release(vm,&_o);
	_o = t;
}

bool SquirrelObject::SetDelegate(SquirrelObject &obj)
{
	if(obj.GetType() == OT_TABLE ||
		obj.GetType() == OT_NULL) {
			switch(_o._type) {
				case OT_USERDATA:
				case OT_TABLE:
					sq_pushobject(vm,_o);
					sq_pushobject(vm,obj._o);
					if(SQ_SUCCEEDED(sq_setdelegate(vm,-2)))
						return true;
					break;
			}
		}
	return false;
}

SquirrelObject SquirrelObject::GetDelegate()
{
	SquirrelObject ret(vm);
	if(_o._type == OT_TABLE || _o._type == OT_USERDATA)
	{
		sq_pushobject(vm,_o);
		sq_getdelegate(vm,-1);
		ret.AttachToStackObject(-1);
		sq_pop(vm,2);
	}
	return ret;
}

bool SquirrelObject::IsNull() const
{
	return sq_isnull(_o);
}

bool SquirrelObject::IsNumeric() const
{
	return sq_isnumeric(_o) ? true : false;
}

int SquirrelObject::Len() const
{
	int ret = 0;
	if(sq_isarray(_o) || sq_istable(_o) || sq_isstring(_o)) {
		sq_pushobject(vm,_o);
		ret = sq_getsize(vm,-1);
		sq_pop(vm,1);
	}
	return ret;
}

#define _SETVALUE_INT_BEGIN \
	bool ret = false; \
	int top = sq_gettop(vm); \
	sq_pushobject(vm,_o); \
	sq_pushinteger(vm,key);

#define _SETVALUE_INT_END \
	if(SQ_SUCCEEDED(sq_rawset(vm,-3))) { \
		ret = true; \
	} \
	sq_settop(vm,top); \
	return ret;

bool SquirrelObject::SetValue(SQInteger key,const SquirrelObject &val)
{
	_SETVALUE_INT_BEGIN
	sq_pushobject(vm,val._o);
	_SETVALUE_INT_END
}

bool SquirrelObject::SetValue(int key,int n)
{
	_SETVALUE_INT_BEGIN
	sq_pushinteger(vm,n);
	_SETVALUE_INT_END
}

bool SquirrelObject::SetValue(int key,float f)
{
	_SETVALUE_INT_BEGIN
	sq_pushfloat(vm,f);
	_SETVALUE_INT_END
}

bool SquirrelObject::SetValue(int key,const SQChar *s)
{
	_SETVALUE_INT_BEGIN
	sq_pushstring(vm,s,-1);
	_SETVALUE_INT_END
}

bool SquirrelObject::SetValue(int key,bool b)
{
	_SETVALUE_INT_BEGIN
	sq_pushbool(vm,b);
	_SETVALUE_INT_END
}

bool SquirrelObject::SetValue(const SquirrelObject &key,const SquirrelObject &val)
{
	bool ret = false;
	int top = sq_gettop(vm);
	sq_pushobject(vm,_o);
	sq_pushobject(vm,key._o);
	sq_pushobject(vm,val._o);
	if(SQ_SUCCEEDED(sq_rawset(vm,-3))) {
		ret = true;
	}
	sq_settop(vm,top);
	return ret;
}

#define _SETVALUE_STR_BEGIN \
	bool ret = false; \
	int top = sq_gettop(vm); \
	sq_pushobject(vm,_o); \
	sq_pushstring(vm,key,-1);

#define _SETVALUE_STR_END \
	if(SQ_SUCCEEDED(sq_rawset(vm,-3))) { \
		ret = true; \
	} \
	sq_settop(vm,top); \
	return ret;

bool SquirrelObject::SetValue(const SQChar *key,const SquirrelObject &val)
{
	_SETVALUE_STR_BEGIN
	sq_pushobject(vm,val._o);
	_SETVALUE_STR_END
}

bool SquirrelObject::SetValue(const SQChar *key,int n)
{
	_SETVALUE_STR_BEGIN
	sq_pushinteger(vm,n);
	_SETVALUE_STR_END
}

bool SquirrelObject::SetValue(const SQChar *key,float f)
{
	_SETVALUE_STR_BEGIN
	sq_pushfloat(vm,f);
	_SETVALUE_STR_END
}

bool SquirrelObject::SetValue(const SQChar *key,const SQChar *s)
{
	_SETVALUE_STR_BEGIN
	sq_pushstring(vm,s,-1);
	_SETVALUE_STR_END
}

bool SquirrelObject::SetValue(const SQChar *key,bool b)
{
	_SETVALUE_STR_BEGIN
	sq_pushbool(vm,b);
	_SETVALUE_STR_END
}


SQObjectType SquirrelObject::GetType()
{
	return _o._type;
}

bool SquirrelObject::GetSlot(int key) const
{
	sq_pushobject(vm,_o);
	sq_pushinteger(vm,key);
	if(SQ_SUCCEEDED(sq_get(vm,-2))) {
		return true;
	}
	
	return false;
}


SquirrelObject SquirrelObject::GetValue(int key)const
{
	SquirrelObject ret(vm);
	if(GetSlot(key)) {
		ret.AttachToStackObject(-1);
		sq_pop(vm,1);
	}
	sq_pop(vm,1);
	return ret;
}

float SquirrelObject::GetFloat(int key) const
{
	float ret = 0.0f;
	if(GetSlot(key)) {
		sq_getfloat(vm,-1,&ret);
		sq_pop(vm,1);
	}
	sq_pop(vm,1);
	return ret;
}

int SquirrelObject::GetInt(int key) const
{
	SQInteger ret = 0;
	if(GetSlot(key)) {
		sq_getinteger(vm,-1,&ret);
		sq_pop(vm,1);
	}
	sq_pop(vm,1);
	return (int)ret;
}

const SQChar *SquirrelObject::GetString(int key) const
{
	const SQChar *ret = 0;
	if(GetSlot(key)) {
		sq_getstring(vm,-1,&ret);
		sq_pop(vm,1);
	}
	sq_pop(vm,1);
	return ret;
}

bool SquirrelObject::GetBool(int key) const
{
	SQBool ret = false;
	if(GetSlot(key)) {
		sq_getbool(vm,-1,&ret);
		sq_pop(vm,1);
	}
	sq_pop(vm,1);
	return ret?true:false;
}

bool SquirrelObject::Exists(const SQChar *key) const
{
	bool ret = false;
	if(GetSlot(key)) {
		ret = true;
	}
	sq_pop(vm,1);
	return ret;
}
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

bool SquirrelObject::GetSlot(const SQChar *name) const
{
	sq_pushobject(vm,_o);
	sq_pushstring(vm,name,-1);
	if(SQ_SUCCEEDED(sq_get(vm,-2))) {
		return true;
	}
	
	return false;
}

SquirrelObject SquirrelObject::GetValue(const SQChar *key)const
{
	SquirrelObject ret(vm);
	if(GetSlot(key)) {
		ret.AttachToStackObject(-1);
		sq_pop(vm,1);
	}
	sq_pop(vm,1);
	return ret;
}

float SquirrelObject::GetFloat(const SQChar *key) const
{
	float ret = 0.0f;
	if(GetSlot(key)) {
		sq_getfloat(vm,-1,&ret);
		sq_pop(vm,1);
	}
	sq_pop(vm,1);
	return ret;
}

int SquirrelObject::GetInt(const SQChar *key) const
{
	SQInteger ret = 0;
	if(GetSlot(key)) {
		sq_getinteger(vm,-1,&ret);
		sq_pop(vm,1);
	}
	sq_pop(vm,1);
	return (int)ret;
}

const SQChar *SquirrelObject::GetString(const SQChar *key) const
{
	const SQChar *ret = 0;
	if(GetSlot(key)) {
		sq_getstring(vm,-1,&ret);
		sq_pop(vm,1);
	}
	sq_pop(vm,1);
	return ret;
}

bool SquirrelObject::GetBool(const SQChar *key) const
{
	SQBool ret = false;
	if(GetSlot(key)) {
		sq_getbool(vm,-1,&ret);
		sq_pop(vm,1);
	}
	sq_pop(vm,1);
	return ret?true:false;
}

SQUserPointer SquirrelObject::GetInstanceUP(SQUserPointer tag) const
{
	SQUserPointer up = 0;
	sq_pushobject(vm,_o);
	sq_getinstanceup(vm,-1,(SQUserPointer*)&up,(SQUserPointer)tag);
	sq_pop(vm,1);
	return up;
}

bool SquirrelObject::SetInstanceUP(SQUserPointer up)
{
	if(!sq_isinstance(_o)) return false;
	sq_pushobject(vm,_o);
	sq_setinstanceup(vm,-1,up);
	sq_pop(vm,1);
	return true;
}

SquirrelObject SquirrelObject::GetAttributes(const SQChar *key)
{
	SquirrelObject ret(vm);
	int top = sq_gettop(vm);
	sq_pushobject(vm,_o);
	if(key)
		sq_pushstring(vm,key,-1);
	else
		sq_pushnull(vm);
	if(SQ_SUCCEEDED(sq_getattributes(vm,-2))) {
		ret.AttachToStackObject(-1);
	}
	sq_settop(vm,top);
	return ret;
}

bool SquirrelObject::BeginIteration()
{
	if(!sq_istable(_o) && !sq_isarray(_o) && !sq_isclass(_o))
		return false;
	sq_pushobject(vm,_o);
	sq_pushnull(vm);
	return true;
}

bool SquirrelObject::Next(SquirrelObject &key,SquirrelObject &val)
{
	if(SQ_SUCCEEDED(sq_next(vm,-2))) {
		key.AttachToStackObject(-2);
		val.AttachToStackObject(-1);
		sq_pop(vm,2);
		return true;
	}
	return false;
}

const SQChar* SquirrelObject::ToString()
{
	return sq_objtostring(&_o);
}

SQInteger SquirrelObject::ToInteger()
{
	return sq_objtointeger(&_o);
}

SQFloat SquirrelObject::ToFloat()
{
	return sq_objtofloat(&_o);
}

bool SquirrelObject::ToBool()
{
	//<<FIXME>>
	return _o._unVal.nInteger?true:false;
}

void SquirrelObject::EndIteration()
{
	sq_pop(vm,2);
}
