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


		#include "core/renderer_resource_factory.h"
		#include "core/renderer.h"
		#include "core/geometry.h"
		#include "core/shader.h"
		#include "core/shader_tree.h"
		#include "core/shader_tree_to_shader.h"
		#include "core/resource_geometry_generator.h"
		#include "picture/pict.h"
		#include "picture/pict_io.h"
		#include "metafile/nml_object.h"
		#include "filesystem/filesystem.h"

		using namespace GS;
		using namespace GS::NML;
		using namespace GS::Render;


//------------------------------------------------------------------------------
Geometry		*RendererResourceFactory::NewGeometry() { return renderer.NewGeometry(); }
Material		*RendererResourceFactory::NewMaterial() { return renderer.NewMaterial(); }
Texture			*RendererResourceFactory::NewTexture() { return renderer.NewTexture(); }
Shader			*RendererResourceFactory::NewShader() { return renderer.NewShader(); }
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
static bool		DoLoadResource(ResourceFactory &rf, Geometry *r, const char *uri)
{
	Core::Geometry g;
	g.name = uri;

	if	(Core::GeometryGenerator::IsGenerated(uri))
	{
		if	(!Core::GeometryGenerator::Generate(uri, g))
			return false;
	}		
	else
		if	(!LoadFromFile(g, uri))
			return false;

	return r->Create(rf, g);
}
static bool		DoLoadResource(ResourceFactory &rf, Material *r, const char *uri)
{
	Core::Material m;
	m.name = uri;

	if	(!LoadFromFile(m, uri))
		return false;

	return r->Create(rf, m);
}
static bool		DoLoadResource(ResourceFactory &rf, Shader *r, const char *uri)
{
	Core::Shader s;
	s.name = uri;

	File file;
	if	(!Parser::Load(uri, file))
		return false;

	if	(Tag *tag = file.GetTag("Shader"))
	{
		if	(!s.FromMetaTag(*tag))
			return false;
	}
	else
	{
		Core::ShaderTree shader_tree;
		if	(!shader_tree.FromMetaTag(*tag) || !Core::ConvertShaderTreeToShader(shader_tree, s))
			return false;
	}

	return r->Create(rf, s);
}
static bool		DoLoadResource(ResourceFactory &rf, Texture *r, const char *uri)
{
	// Read optional texture parameters.
	LoadFromFile(r->parm, TextureParm::GetParmFileName(uri), false);

	// Read data.
	if	(!r->LoadCooked(uri))
	{
		Picture picture;
		if	(!PictureIO::Get().Load(picture, uri))
			return false;

		picture.Convert(PixelFormat::RGBA8);
		r->parm.Apply(picture);

		if	(!r->Create(picture))
			return false;
	}

	r->parm.Apply(*r);
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
static Data	*GeometryFactory(ResourceFactory &rf) { return rf.NewGeometry(); }
static Data	*MaterialFactory(ResourceFactory &rf) { return rf.NewMaterial(); }
static Data	*TextureFactory(ResourceFactory &rf) { return rf.NewTexture(); }
static Data	*ShaderFactory(ResourceFactory &rf) { return rf.NewShader(); }
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
template <class T> T *LoadResourceCommonSeq(const char *uri, ResourceFactory &rf, Data *(factory)(ResourceFactory &), T *r, const char *errored_uri)
{
	if	(!uri)
		return NULL;

	String name(uri);
	name.FileCleanName();

	// Load sequence.
	if	(rf.event_handler)
	{
		rf.event_handler->OpenLoad();
		rf.event_handler->LoadProgress(String::Format("Loading resource '%s'...", name.c_str()));
	}

	T *res = r ? r : (T *)factory(rf);
	if	(!res)
		return NULL;

	if	(!DoLoadResource(rf, res, name))
	{
		if	(errored_uri && !DoLoadResource(rf, res, errored_uri))
			if	(!r)	// note: do not erase res if it was externally provided (through r)!
				_safe_delete(res);
	}

	if	(res)
		res->name = name;

	if	(rf.event_handler)
		rf.event_handler->EndLoad();

	return res;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
Geometry		*RendererResourceFactory::LoadGeometry(const char *uri, bool, Geometry *g)
{	return LoadResourceCommonSeq <Geometry> (uri, *this, GeometryFactory, g, NULL);	}
Material		*RendererResourceFactory::LoadMaterial(const char *uri, bool, Material *m)
{	return LoadResourceCommonSeq <Material> (uri, *this, MaterialFactory, m, "@core/builtin/material/missing.nmm");	}
Texture			*RendererResourceFactory::LoadTexture(const char *uri, bool, Texture *t)
{	return LoadResourceCommonSeq <Texture> (uri, *this, TextureFactory, t, "@core/builtin/maps/missing_texture.png");	}
Shader			*RendererResourceFactory::LoadShader(const char *uri, bool, Shader *s)
{	return LoadResourceCommonSeq <Shader> (uri, *this, ShaderFactory, s, "@core/builtin/shader/missing_shader.nsa");	}
//------------------------------------------------------------------------------
