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


		#include "ui/ui.h"
		#include "ui/ui_window.h"
		#include "ui/ui_group.h"
		#include "core/tool_mode.h"
		#include "script/scripted_object.h"
		#include "script/script_unit.h"
		#include "timing/benchmark.h"
		#include "metafile/nml.h"
		#include "log/log.h"

		using namespace GS::NML;
		using namespace GS::S2D;


//------------------------------------------------------------------------------
bool				Scene::FromMetaFileStoreGroup(const char *path, Group **group, uint flag, GS::ToolMode tool_mode, int load_level)
{
	NML::File file;
	if	(!NML::Parser::Load(path, file))
		return false;

	if	(Tag *scene_tag = file.GetTag("Scene2D"))
	{
		if	(FromMetaTagStoreGroup(*scene_tag, group, flag, tool_mode, load_level))
			name = path;
	}
	else
		__ERR__(__LOG_E__ << "No <Scene2D> tag in '" << path << "'.\n", false)

	if	(group && group[0])
		group[0]->name = path;

	return true;
}
static	bool	ShouldLoadItem(GS::ToolMode mode, Tag *pt, uint flag, int load_level)
{
	if	(pt->GetTag("Item:ToolSpecific;") || pt->GetTag("Item:Helper;"))
	{
		if	(!(flag & SceneIOHelper))
			return false;

		// Do not load tool specific items in no tool or project preview modes.
		if	((mode == GS::NoTool) || (mode == GS::ToolProjectPreview))
			return false;

		// Do not load tool specific items in no tool mode above the first load level.
		if	(load_level > 0)
			return false;
	}
	return true;
}
template <class T> static T *LoadItemDerived(Scene *scene, T *i, GS::Map <uint, uint> &uid_map, Tag *t, uint type_flag, const uint load_flag, int load_level, GS::ToolMode tool_mode)
{
	if	(!(load_flag & type_flag) || !ShouldLoadItem(tool_mode, t, load_flag, load_level))
	{
		delete i;
		return NULL;
	}

	scene->SetupItemComponents(i);

	if	(i->FromMetaTag(*t))
	{
		uint olduid = i->GetUid();			// uid from source scene
		scene->AddItem(i, false);			// add to live scene
		uid_map.Add(olduid, i->GetUid());	// store map from source to live scene
	}
	return i;
}
bool				Scene::FromMetaTagStoreGroup(const Tag &tag, Group **group, uint load_flag, GS::ToolMode tool_mode, int load_level)
{
	if	(tag.name != "Scene2D")
		__ERR__(__LOG_E__ << "Could not parse UI scene, incorrect root tag (" << tag.name << ").\n", false)

	Benchmark bench(true);

	// Read items and setup uid remap array.
	Map <uint, uint> uid_map;

	if	(Tag *itag = tag.GetTag("Items"))
	{
		// Item count in scene.
		uint item_to_load = 0;
		NMLTagForeach(pt, *itag)
			if	(Tag *item_tag = pt->GetTag("Item;"))
				item_to_load++;

		NMLTagForeach(pt, *itag)
		{
			Item *citem = NULL;

					if	(pt->name == "Sprite")
					citem = LoadItemDerived(this, new Sprite, uid_map, pt, SceneIOSprite, load_flag, load_level, tool_mode);
			else	if	(pt->name == "Window")
					citem = LoadItemDerived(this, new Window, uid_map, pt, SceneIOWindow, load_flag, load_level, tool_mode);
			else
					__LOG_W__ << "Unexpected <" << pt->name << "> sub-tag in <Items>.\n";

			if	(citem)
			{
				// Add item to the scene group.
				if	(group && group[0])
					group[0]->item_list.Add(citem);
			}
		}
	}

	// Script unit.
	if	(load_flag & SceneIOScript)
	{
		if	(Tag *stag = tag.GetTag("ScriptedObject"))
			scripted_object->FromMetaTag(*stag);

	#if	1	// Compatibility
		else
			if	(Tag *stag = tag.GetTag("ScriptUnit"))
				if	(Script::Unit *unit = scripted_object->AddUnit(scripted_object->NewUnit()))
					unit->FromMetaTag(*stag);
	#endif
	}

	// Motion sets.
	if	(load_flag & SceneIOMotion)
		if	(Tag *ctag = tag.GetTag("SceneMotionContainer"))
			motion.FromMetaTag(*ctag, &uid_map);

	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Scene::IsItemToBeSaved(Item *i, const Group *group, uint flag) const
{
	if	(!i)
		return false;

	// Filter out irrelevant items.
	if	(group && !group->IsMember(i))
		return false;

	switch (i->GetItemType())
	{
		case Item::Type_Sprite:	if (!(flag & SceneIOSprite)) return false;	break;
		case Item::Type_Window:	if (!(flag & SceneIOWindow)) return false;	break;
	}
	return true;
}
Tag			*Scene::LinkInfoAsMetaTag(Item *i, const Group *group, uint flag) const
{
	if	(!IsItemToBeSaved(i, group, flag))
		return NULL;

	Item *l = i->GetParent();
	if	(!l)
		return NULL;

	if	(!IsItemToBeSaved(l, group, flag))
		return NULL;

	// Save link information.
	Tag *tag = new Tag("Link");
	if	(tag)
	{
		tag->AddChild("Item", i->GetUid());
		tag->AddChild("Link", l->GetUid());
	}
	return tag;
}
Tag			*Scene::AsMetaTag(const Group *group, uint save_flag, GS::ToolMode tool_mode) const
{
	Tag *root = new Tag("Scene2D");
	if	(!root)
		__ERR__(__LOG_E__ << "Could not create root tag to serialize.\n", NULL)

	// Write script object.
	if	(scripted_object.IsValid() && !group)
		root->AddChild(scripted_object->AsMetaTag());

	// Write motion sets.
	if	(save_flag & SceneIOMotion)
		root->AddChild(motion.AsMetaTag());

	// Write managed items.
	if	(Tag *itag = root->AddChild("Items"))
		ListForeachPtr(Item *, i, item_list)
		{
			if	(group && !group->item_list.Find(i))
				continue;

			switch (i->GetItemType())
			{
				case Item::Type_Sprite:		if	(!(save_flag & SceneIOSprite)) continue;	break;
				case Item::Type_Window:		if	(!(save_flag & SceneIOWindow)) continue;	break;
			}

			itag->AddChild(i->AsMetaTag());
		}
	else
		__ERR__(__LOG_E__ << "Failed to allocate <Items> root tag.\n", NULL);

	// Write link informations.
	if	(Tag *ltag = root->AddChild("Links"))
		ListForeachPtr(Item *, i, item_list)
			ltag->AddChild(LinkInfoAsMetaTag(i, group, save_flag));
	else
		__ERR__(__LOG_E__ << "Failed to allocate <Links> root tag.\n", NULL)

	return root;
}
//------------------------------------------------------------------------------
