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


		#include "tools/resource_explorer.h"
		#include "metafile/nml.h"
		#include "platform.h"
		#include "filesystem/filesystem.h"

		using namespace GS::Resource;
		using namespace GS::NML;
		using GS::String;


//------------------------------------------------------------------------------
DependencyRule scene3d_dependency_rules[] =
{
	// Instances
	{	"Items/*Instance/Template", "Scene"	},

	// Terrains
	{	"Items/*MTerrain/Terrain/Blendmap", "Texture"	},
	{	"Items/*MTerrain/Terrain/Blendmap", "Shader"	},
	{	"Items/*MTerrain/Terrain/MaterialName", "Material"	},
	{	"Items/*MTerrain/Terrain/Heightmap/Data", "Data"	},
	{	"Items/*MTerrain/Terrain/Layers/*Layer/Diffuse", "Texture"	},
	{	"Items/*MTerrain/Terrain/Layers/*Layer/Normal", "Texture"	},
	{	"Items/*MTerrain/Terrain/Layers/*Layer/Specular", "Texture"	},
	{	"Items/*MTerrain/Terrain/Layers/*Layer/Self", "Texture"	},

	// Lights
	{	"Items/*MLight/Light/ProjectionMap", "Texture"	},

	// Objects
	{	"Items/*MObject/Object/Geometry", "Geometry"	},

	// All items
	{	"Items/*/MItem/ScriptedObject/*ScriptUnit/Script", "Script"	},	// legacy
	{	"Items/*/MItem/ScriptedObject/*ScriptUnit/ScriptPath", "Script"	},
	{	"Items/*/MItem/PhysicItem/Shapes/*PhysicShape/Mesh", "Geometry"	},

	// Scene
	{	"ScriptedObject/*ScriptUnit/Script", "Script"	},	// legacy
	{	"ScriptedObject/*ScriptUnit/ScriptPath", "Script"	},
	{	0, 0	}
};
DependencyRule geometry_dependency_rules[] =
{
	{	"Materials/*MaterialRef", "Material"	},
	{	"Materials/*MaterialRefEx/Name", "Material"	},

	{	"LodProxy", "Geometry"	},
	{	"ShadowProxy", "Geometry"	},

	{	0, 0	}
};
DependencyRule material_dependency_rules[] =
{
	{	"*TextureStage/Texture", "Texture"	},
	{	"Shader", "Shader"	},
	{	0, 0	}
};
DependencyRule shader_tree_dependency_rules[] =
{
	{	"Map/*Entry/Param/Texture", "Texture"	},
	{	0, 0	}
};
DependencyRule shader_dependency_rules[] =
{
	{	"Input/*Uniform/Texture", "Texture"	},
	{	0, 0	}
};
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
ExplorerRule resource_rules[] =
{
	{	"Scene3d", "Scene", scene3d_dependency_rules	},
	{	"Geometry", "Geometry", geometry_dependency_rules	},
	{	"Material", "Material", material_dependency_rules	},
	{	"Shader Tree", "ShaderMap", shader_tree_dependency_rules	},
	{	"Shader", "Shader", shader_dependency_rules	},
	{	0, 0	}
};
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
const char			*Explorer::DependencyTag::GetName() const
{	return tag->GetString();	}
void				Explorer::DependencyTag::SetName(const char *n)
{	tag->SetString(n);	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool resource_filter(const Explorer::Resource *r, const char *name) { return r->name == name; }
bool dependency_filter(const Explorer::Dependency *d, const char *name) { return !String::strccmp(d->GetName(), name); }
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
const Explorer::Dependency	*Explorer::Resource::FindDependency(const char *n) const
{
	return ListFindEx(dependencies, dependency_filter, n);
}
bool				Explorer::Resource::HasMissingDependencies() const
{
	ListForeachPtr(Dependency *, dep, dependencies)
		if	(dep->missing)
			return true;
	return false;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Explorer::ExploreResourceRuleTags(const GS::StringList &tags, uint pos, Tag *tag, Resource *r, int depth)
{
	if	(pos == tags.GetCount())				// end of path
	{
		if	(tag->GetString())
		{
			String name = tag->GetString();

//			if	(!r->FindDependency(name))
			{
				DependencyTag *dependency = new DependencyTag(tag);
				r->dependencies.Add(dependency);

				if	(name.StartsWith("@sys/"))	// engine generated resource
					dependency->missing = false;

				else if	(name.StartsWith("@core/"))
					dependency->missing = !Platform::Get().io->Exists(name);

				// Assert dependency is found.
				else if (!name.IsAbsolutePath())
				{
					// Check in project path.
					if	(!project_path.IsEmpty())
					{
						String _name = String::Format("%s/%s", project_path.c_str(), name.c_str()).CleanFilePath();
						if	((dependency->missing = !Platform::Get().io->Exists(_name)) == false)
							name = _name;
					}

					// Check in core path if still missing.
					if	(dependency->missing)
						if	(!core_path.IsEmpty())
						{
							String _name = String::Format("%s/%s", core_path.c_str(), name.c_str()).CleanFilePath();
							if	((dependency->missing = !Platform::Get().io->Exists(_name)) == false)
								name = _name;
						}
				}
				ExploreResource(name, project_path, core_path, depth - 1);
			}
		}
	}
	else
	{
		const String &t = tags[pos];

		if	(t.StartsWith("*"))					// iterate current tag
		{
			String match = t.Mid(1);
			NMLTagForeach(c, *tag)
				if	(match.IsEmpty() || (c->name == match))
					ExploreResourceRuleTags(tags, pos + 1, c, r, depth);
		}
		else
		{
			if	(Tag *c = tag->GetTag(t))	// no tag means rule not met
				ExploreResourceRuleTags(tags, pos + 1, c, r, depth);
		}
	}
}
bool				Explorer::ExploreResourceRule(ExplorerRule &rule, Resource *r, int depth)
{
	Tag *tag = r->file->GetTag(rule.root);
	if	(!tag)
		return false;

	r->type = rule.type;

	for (int n = 0; rule.rules[n].type; ++n)
	{
		// Split rule tags.
		StringList tags;
		String(rule.rules[n].path).Split("/", tags);

		ExploreResourceRuleTags(tags, 0, tag, r, depth);
	}
	return true;
}
Explorer::Resource *Explorer::ExploreResource(const char *name, const char *_project_path, const char *_core_path, int depth)
{
	if	(depth <= 0)
		return NULL;

	project_path = _project_path;
	core_path = _core_path;

	// Metafile.
	if	(!Parser::IsMetafile(name))
		return NULL;

	// Check if that resource has already been loaded.
	if	(Resource *_r = ListFindEx(resources, resource_filter, name))
		return _r;

	// Load file.
	AutoPtr <Resource> r(new Resource(name, "Null"));
	r->file = Parser::Load(name);
	if	(r->file.IsNull())
		return NULL;

	// Explore resource.
	for (int n = 0; resource_rules[n].type; ++n)
		if	(ExploreResourceRule(resource_rules[n], r, depth))
		{
			resources.Add(r);
			return r.Detach();
		}

	return NULL;
}
const Explorer::Resource *Explorer::FindResource(const char *name) const
{
	return ListFindEx(resources, resource_filter, name);
}
void				Explorer::Clear()
{
	resources.Clear();
}
//------------------------------------------------------------------------------
