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


		#include "binding_helpers.h"
		#include "metafile/nml.h"

		using namespace GS;
		using namespace GS::NML;
		using namespace GS::Script;


//-----------------------------------------------------------------------------
SQInteger			MetafileGetRoots(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(metafile, File, typetag_Metafile)

	sq_newarray(vm, 0);
	if	(metafile)
	{
		NMLFileForeach(tag, *metafile)
		{
			CObject::Push(vm, tag, typetag_Metatag, false);
			sq_arrayappend(vm, -2);
		}
	}
	return 1;
}
SQInteger			MetatagGetChildren(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(metatag, Tag, typetag_Metatag)

	sq_newarray(vm, 0);
	if	(metatag)
	{
		NMLTagForeach(tag, *metatag)
		{
			CObject::Push(vm, tag, typetag_Metatag, false);
			sq_arrayappend(vm, -2);
		}
	}
	return 1;
}

SQInteger			MetafileNew(HSQUIRRELVM vm)
{	__SQ_RETURNMANAGEDSAFEPTR(new File, typetag_Metafile)		}
SQInteger			MetafileDelete(HSQUIRRELVM vm)
{	__SQ_RETURN		}

SQInteger			MetafileLoadFromString(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(mf, File, typetag_Metafile)
	__SQ_GETSTRING(file)
	bool success = Parser::LoadFromMemory((char *)file, String::strlen(file), *mf);
	__SQ_GETEND
	__SQ_RETURNBOOL(success)
}
SQInteger			MetafileLoad(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(mf, File, typetag_Metafile)
	__SQ_GETSTRING(uri)
	bool success = Parser::Load(uri, *mf);		// take care of the string
	__SQ_GETEND
	__SQ_RETURNBOOL(success)
}
SQInteger			MetafileSave(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(mf, File, typetag_Metafile)
	__SQ_GETSTRING(uri)
	bool success = Parser::Save(uri, *mf);
	__SQ_GETEND
	__SQ_RETURNBOOL(success)
}

//-----------------------------------------------------------------------------
static Tag	*CreateMetatagFromSquirrelStackEntry(HSQUIRRELVM vm, int idx, const char *path)
{
	Tag *tag = NULL;
	switch (sq_gettype(vm, idx))
	{
		case OT_NULL:
			tag = new Tag(path);
			break;

		case OT_INTEGER:
		{
			SQInteger value;
			sq_getinteger(vm, idx, &value);
			tag = new Tag(path, (int)value);
		}
		break;

		case OT_FLOAT:
		{
			float value;
			sq_getfloat(vm, idx, &value);
			tag = new Tag(path, value);
		}
		break;

		case OT_BOOL:
		{
			SQBool value;
			sq_getbool(vm, idx, &value);
			tag = new Tag(path, value ? true : false);
		}
		break;

		case OT_STRING:
		{
			const char *value;
			sq_getstring(vm, idx, &value);
			tag = new Tag(path, value);
		}
		break;

		case OT_TABLE:
		case OT_ARRAY:
		default:
			__LOG_E__ << "Cannot convert input Squirrel type to metatag value.\n";
			break;
	}
	return tag;
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
SQInteger			MetafileAddRoot(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(mf, File, typetag_Metafile)
	__SQ_GETSTRING(path)
	Tag *tag = new Tag(path);
	if	(!tag)
		return sq_throwerror(vm, "Failed to allocate metatag.");
	mf->AddRoot(tag);
	__SQ_GETEND
	__SQ_RETURNSAFEPTR(tag, typetag_Metatag)
}
SQInteger			MetatagAddChild(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(itg, Tag, typetag_Metatag)
	__SQ_GETSTRING(path)
	Tag *tag = new Tag(path);
	if	(!tag)
		return sq_throwerror(vm, "Failed to allocate metatag.");
	itg->AddChild(tag);
	__SQ_GETEND
	__SQ_RETURNSAFEPTR(tag, typetag_Metatag)
}
SQInteger			MetatagDeleteChild(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(tag, Tag, typetag_Metatag)
	__SQ_GETSAFEPTR(child, Tag, typetag_Metatag)
	__SQ_GETEND

	if	(!tag->RemoveTag(child))
		return sq_throwerror(vm, "Child tag does not belong to this tag.");

	__SQ_INVALIDATENATIVEREF(child);
	_safe_delete(child); // tags are not managed
	__SQ_RETURN
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
SQInteger			MetafileAddRootWithValue(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(mf, File, typetag_Metafile)
	__SQ_GETSTRING(path)
	Tag *tag = CreateMetatagFromSquirrelStackEntry(vm, __SQ_STACKPOS, path);
	__SQ_GETUPDATESTACK
	__SQ_GETEND
	if	(!tag)
		return sq_throwerror(vm, "Failed to add root tag to metafile.");
	mf->AddRoot(tag);
	__SQ_RETURNSAFEPTR(tag, typetag_Metatag)
}
SQInteger			MetatagAddChildWithValue(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(itg, Tag, typetag_Metatag)
	__SQ_GETSTRING(path)
	Tag *tag = CreateMetatagFromSquirrelStackEntry(vm, __SQ_STACKPOS, path);
	__SQ_GETUPDATESTACK
	__SQ_GETEND
	if	(!tag)
		return sq_throwerror(vm, "Failed to add child tag.");
	itg->AddChild(tag);
	__SQ_RETURNSAFEPTR(tag, typetag_Metatag)
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
SQInteger			MetafileGetTag(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(mf, File, typetag_Metafile)
	__SQ_GETSTRING(path)
	Tag *tag = mf->GetTag(path);
	__SQ_GETEND
	__SQ_RETURNSAFEPTR(tag, typetag_Metatag)
}
SQInteger			MetafileGetTypedTag(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(mf, File, typetag_Metafile)
	__SQ_GETSTRING(path)
	__SQ_GETINT(type)
	Tag *tag = mf->GetTypedTag(path, (GS::Variant::Type)type);
	__SQ_GETEND
	__SQ_RETURNSAFEPTR(tag, typetag_Metatag)
}
SQInteger			MetatagGetTag(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(mf, Tag, typetag_Metatag)
	__SQ_GETSTRING(path)
	Tag *tag = mf->GetTag(path);
	__SQ_GETEND
	__SQ_RETURNSAFEPTR(tag, typetag_Metatag)
}
SQInteger			MetatagGetTypedTag(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(mf, Tag, typetag_Metatag)
	__SQ_GETSTRING(path)
	__SQ_GETINT(type)
	Tag *tag = mf->GetTypedTag(path, (GS::Variant::Type)type);
	__SQ_GETEND
	__SQ_RETURNSAFEPTR(tag, typetag_Metatag)
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
SQInteger			MetatagGetType(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(tag, Tag, typetag_Metatag)
	__SQ_RETURNINT((int)tag->GetValue().GetType())
}
SQInteger			MetatagGetName(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(tag, Tag, typetag_Metatag)
	__SQ_RETURNSTRING(tag->name.c_str())
}
SQInteger			MetatagSetName(HSQUIRRELVM vm)
{	
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(tag, Tag, typetag_Metatag)
	__SQ_GETSTRING(_name)
	tag->name = _name;
	__SQ_GETEND
	__SQ_RETURN
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
SQInteger			MetatagGetValue(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(tag, Tag, typetag_Metatag)

	switch (tag->GetValue().GetType())
	{
		default:
		case GS::Variant::VariantNone:
		case GS::Variant::VariantBinary:
			break;

		case GS::Variant::VariantBool:
			__SQ_RETURNBOOL(tag->GetBool())
		case GS::Variant::VariantInteger:
			__SQ_RETURNINT(tag->GetInteger())
		case GS::Variant::VariantFloat:
			__SQ_RETURNFLOAT(tag->GetReal())
		case GS::Variant::VariantString:
			__SQ_RETURNSTRING(tag->GetString())
	}

	__LOG_E__ << "Cannot get value from tag '" << tag->name << "'. Unsupported type.\n";
	__SQ_RETURNINT(-1)
}
SQInteger			MetatagSetValue(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(tag, Tag, typetag_Metatag)

	switch (sq_gettype(vm, -1))
	{
		case OT_INTEGER:
		{
			__SQ_GETINT(value)
			tag->SetInteger(value);
		}
		break;

		case OT_FLOAT:
		{
			__SQ_GETFLOAT(value)
			tag->SetReal(value);
		}
		break;

		case OT_BOOL:
		{
			__SQ_GETBOOL(value)
			tag->SetBool(value ? true : false);
		}
		break;

		case OT_STRING:
		{
			__SQ_GETSTRING(value)
			tag->SetString(value);
		}
		break;

		case OT_NULL:
		case OT_TABLE:
		case OT_ARRAY:
		default:
			return sq_throwerror(vm, "Type cannot be converted to metatag value.");
	}

	__SQ_GETEND
	__SQ_RETURN
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
void		RegisterNMLBinding(HSQUIRRELVM vm)
{
/*#
	Topic: Metafile
	Type: Metafile
	Type: Metatag
	Desc: A metafile is very similar to an XML file, it is mostly used to store data in a structured way.
#*/

/*#
	Section: MetaFile
	Desc: File functions
#*/
	/*#
		Func: MetafileNew
		Proto: Metafile:
		Desc: Create a new metafile, this object is managed, the VM will free it.
	#*/
	sq_register(vm, MetafileNew, "MetafileNew", _SC("."));
	// Obsolete
	sq_register(vm, MetafileDelete, "MetafileDelete", _SC(".x"));
	/*#
		Func: MetafileLoadFromString
		Proto: bool:Metafile,string content
		Desc: Load metafile from a string.
	#*/
	sq_register(vm, MetafileLoadFromString, "MetafileLoadFromString", _SC(".xs"));
	/*#
		Func: MetafileLoad
		Proto: bool:Metafile,string path
		Desc: Load metafile from file system.
	#*/
	sq_register(vm, MetafileLoad, "MetafileLoad", _SC(".xs"));
	/*#
		Func: MetafileSave
		Proto: bool:Metafile,string path
		Desc: Save metafile to file system.
	#*/
	sq_register(vm, MetafileSave, "MetafileSave", _SC(".xs"));
	/*#
		Func: MetafileAddRoot
		Proto: Metatag:Metafile,string tag_name
		Desc: Add metafile root, returns the newly created metatag.
	#*/
	sq_register(vm, MetafileAddRoot, "MetafileAddRoot", _SC(".xs"));
	/*#
		Func: MetafileAddRootWithValue
		Proto: Metatag:Metafile,string tag_name,...
		Desc: Add metafile root, returns the newly created metatag.
	#*/
	sq_register(vm, MetafileAddRootWithValue, "MetafileAddRootWithValue", _SC(".xs."));
	/*#
		Func: MetafileGetRoots
		Proto: array:Metafile
		Desc: Return all root tags of a metafile in an array.
	#*/
	sq_register(vm, MetafileGetRoots, "MetafileGetRoots", _SC(".x"));
	/*#
		Func: MetafileGetTag
		Proto: Metatag:Metafile,string tag_path
		Desc: Get metatag from metafile, the metatag path is formatted as follow: tag:child:tag_id; (eg: 'Pref:Sound:Volume;').
	#*/
	sq_register(vm, MetafileGetTag, "MetafileGetTag", _SC(".xs"));
	/*#
		Func: MetafileGetTypedTag
		Proto: Metatag:Metafile,string tag_path,TagType
		Desc: Get metatag of a specific value type from a metafile.
	#*/
	sq_register(vm, MetafileGetTypedTag, "MetafileGetTypedTag", _SC(".xsi"));

/*#
	Section: MetaTag
	Desc: Tag functions
#*/
	/*#
		Func: MetatagGetName
		Proto: string:Metatag
		Desc: Get metatag name.
	#*/
	sq_register(vm, MetatagGetName, "MetatagGetName", _SC(".x"));
	/*#
		Func: MetatagSetName
		Proto: void:Metatag,String
		Desc: Set metatag name.
	#*/
	sq_register(vm, MetatagSetName, "MetatagSetName", _SC(".xs"));
	/*#
		Func: MetatagGetType
		Proto: TagType:Metatag
		Desc: Get metatag type.
	#*/
	sq_register(vm, MetatagGetType, "MetatagGetType", _SC(".x"));
	/*#
		Func: MetatagGetValue
		Proto: ...:Metatag
		Desc: Get metatag value.
	#*/
	sq_register(vm, MetatagGetValue, "MetatagGetValue", _SC(".x"));
	/*#
		Func: MetatagSetValue
		Proto: void:Metatag,...
		Desc: Set metatag value, the value type is automatically handled.
	#*/
	sq_register(vm, MetatagSetValue, "MetatagSetValue", _SC(".x."));
	
	/*#
		Func: MetatagDeleteChild
		Proto: void:Metatag tag, Metatag child
		Desc: Delete a metatag child tag.
		Note: The removed child tag is freed and all existing script references to it are invalidated.
	#*/
	sq_register(vm, MetatagDeleteChild, "MetatagDeleteChild", _SC(".xx"));
	/*#
		Func: MetatagAddChild
		Proto: Metatag:Metatag,string tag_name
		Desc: Add metatag child, returns the newly created metatag.
	#*/
	sq_register(vm, MetatagAddChild, "MetatagAddChild", _SC(".xs"));
	/*#
		Func: MetatagAddChildWithValue
		Proto: Metatag:Metatag,string tag_name,...
		Desc: Add metatag child, returns the newly created metatag.
	#*/
	sq_register(vm, MetatagAddChildWithValue, "MetatagAddChildWithValue", _SC(".xs."));

	/*#
		Func: MetatagGetTag
		Proto: Metatag:Metatag,string tag_path
		Desc: Find metatag child.
	#*/
	sq_register(vm, MetatagGetTag, "MetatagGetTag", _SC(".xs"));
	/*#
		Func: MetatagGetTypedTag
		Proto: Metatag:Metatag,string tag_path,TagType
		Desc: Find child of a specific value type from a metatag.
	#*/
	sq_register(vm, MetatagGetTypedTag, "MetatagGetTypedTag", _SC(".xsi"));
	/*#
		Func: MetatagGetChildren
		Proto: Array:Metatag
		Desc: Return all children of a metatag in an array.
	#*/
	sq_register(vm, MetatagGetChildren, "MetatagGetChildren", _SC(".x"));

	// Push defines.
	sq_pushroottable(vm);

	/*#
		Enum: TagType
		Values: TagTypeBinary,TagTypeString,TagTypeInteger,TagTypeReal,TagTypeNode,TagTypeNone
	#*/
	sq_pushstring(vm, "TagTypeBinary", -1); sq_pushinteger(vm, GS::Variant::VariantBinary); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "TagTypeInteger", -1); sq_pushinteger(vm, GS::Variant::VariantInteger); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "TagTypeNone", -1); sq_pushinteger(vm, GS::Variant::VariantNone); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "TagTypeReal", -1); sq_pushinteger(vm, GS::Variant::VariantFloat); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "TagTypeString", -1); sq_pushinteger(vm, GS::Variant::VariantString); sq_newslot(vm, -3, true);
#if	1
	sq_pushstring(vm, "TagTypeNode", -1); sq_pushinteger(vm, GS::Variant::VariantNone); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "TagTypeTag", -1); sq_pushinteger(vm, GS::Variant::VariantNone); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "TagTypeShortcut", -1); sq_pushinteger(vm, GS::Variant::VariantNone); sq_newslot(vm, -3, true);
#endif

	sq_pop(vm, 1);
}
//-----------------------------------------------------------------------------
