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


		#include "core/shader.h"
		#include "metafile/nml.h"
		#include "filesystem/filesystem.h"
		#include "platform.h"
		#include "log/log.h"

		using namespace GS::Core;
		using GS::NML::Tag;


//------------------------------------------------------------------------------
void				Shader::ParseVaryingTag(Tag *tag)
{
	if	(!tag || tag->name != "Varying")
		return;

	NMLTagForeach(it, *tag)
		if	(it->name == "Variable")
		{
			Tag *t_name = it->GetTypedTag("Name", Variant::VariantString),
				*t_type = it->GetTypedTag("Type", Variant::VariantString);
			if	(t_name && t_type)
				DeclareVarying(t_name->GetString(), t_type->GetString());
		}
}
void				Shader::ParseInputTag(Tag *tag)
{
	if	(!tag || tag->name != "Input")
		return;

	NMLTagForeach(it, *tag)
	{
		ShaderInput::Type type = ShaderInput::None;
		uint scope = 0;

		// Parse shader input.
		if	(it->name == "Attribute")
		{
			type = ShaderInput::Attribute;
			scope = ShaderInput::Vertex;
		}
		else	if	(it->name == "Uniform")
		{
			type = ShaderInput::Uniform;
			scope = ShaderInput::Pixel;
		}

		if	(type != ShaderInput::None)
		{
			String input_name;
			ShaderInput::DataType data_type = ShaderInput::NoData;
			ShaderInput::Semantic semantic = ShaderInput::Constant;

			if	(Tag *t = it->GetTypedTag("Name", Variant::VariantString))
				input_name = t->GetString();

			if	(Tag *t = it->GetTypedTag("Semantic", Variant::VariantString))
			{
				String	cs(t->GetString());

			#if	1	// Legacy semantic support (changed with 1.3.0).
				if	(cs == "Texture")
					cs = "Texture2D";
				else if	(cs == "NativeTexture")
					cs = "Texture2D";
				else if	(cs == "CubeTexture")
					cs = "TextureCube";
				else if	(cs == "User")
					cs = "Constant";
			#endif

				uint n = 0;
				for	(; n < ShaderInput::LastSemantic; ++n)
					if	(cs == ShaderInput::semantic_desc[n].name)
					{
						semantic = (ShaderInput::Semantic)n;
						data_type = ShaderInput::semantic_desc[semantic].data_type;
						break;
					}

				if	(n == ShaderInput::LastSemantic)
					__LOG_E__ << "Unknown shader input semantic '" << cs << "'.\n";
			}

			if	(Tag *t = it->GetTag("Type"))
			{
				String type(t->GetString());

						if	(type == "float")	data_type = ShaderInput::Float;
				else	if	(type == "vec2")	data_type = ShaderInput::Vector2;
				else	if	(type == "vec3")	data_type = ShaderInput::Vector3;
				else	if	(type == "vec4")	data_type = ShaderInput::Vector4;
				else	if	(type == "mat3")	data_type = ShaderInput::Matrix3;
				else	if	(type == "mat4")	data_type = ShaderInput::Matrix4;
				else
					__LOG_E__ << "Unknown type '" << type << "' for input '" << input_name << "'.\n";
			}

			if	(Tag *t = it->GetTag("Scope"))
			{
				scope = 0;
				if	(t->GetTag("Vertex"))
					scope |= ShaderInput::Vertex;
				if	(t->GetTag("Pixel") || t->GetTag("Fragment"))
					scope |= ShaderInput::Pixel;
			}

			ShaderInput *input = NULL;

			if	(semantic != ShaderInput::LastSemantic)
				input = DeclareInput(input_name, data_type, semantic, type, (ShaderInput::Scope)scope);
			else
				__LOG_W__ << "Unknown shader input semantic in shader '" << name << "'.\n";

			if	(input)
				switch (input->semantic)
				{
					case ShaderInput::Texture2D:
					case ShaderInput::TextureCube:
						if	(Tag *t = it->GetTypedTag("Texture", Variant::VariantString))
							input->parm_t = t->GetString();
						break;

					case ShaderInput::Constant:
						if	(Tag *t = it->GetTag("Vector"))
							input->parm_v.FromMetaTag(*t);
						break;
				}
		}
	}
}
bool				Shader::FromMetaTag(Tag &tag)
{
	if	(tag.name != "Shader")
		return false;

	Clear();

	NMLTagForeach(pt, tag)
	{
				if	(pt->name == "Name")
				name = pt->GetString();

		else	if	(pt->name == "Varying")
				ParseVaryingTag(pt);
		else	if	(pt->name == "Input")
				ParseInputTag(pt);

		else	if	(pt->name == "VertexDeclaration")
				vertex_decl = pt->GetString();
		else	if	(pt->name == "PixelDeclaration")
				pixel_decl = pt->GetString();
		else	if	(pt->name == "GeometryDeclaration")
				geometry_decl = pt->GetString();

		else	if	(pt->name == "VertexSource")
				vertex = pt->GetString();
		else	if	((pt->name == "PixelSource") || (pt->name == "FragmentSource"))
				pixel = pt->GetString();
		else	if	(pt->name == "GeometrySource")
				geometry = pt->GetString();

		else	if	(pt->name == "Vertex")
		{
			Array <char> buffer;
			if	(Platform::Get().io->FileLoad(pt->GetString(), buffer))
				vertex.Set(buffer, &buffer[buffer.GetCount()]);
		}
		else	if	((pt->name == "Pixel") || (pt->name == "Fragment"))
		{
			Array <char> buffer;
			if	(Platform::Get().io->FileLoad(pt->GetString(), buffer))
				pixel.Set(buffer, &buffer[buffer.GetCount()]);
		}
		else	if	(pt->name == "Geometry")
		{
			Array <char> buffer;
			if	(Platform::Get().io->FileLoad(pt->GetString(), buffer))
				geometry.Set(buffer, &buffer[buffer.GetCount()]);
		}
		else
			__LOG_W__ << "Unknown tag '" << pt->name << "' in <Shader>.\n";
	}
	return true;
}
Tag					*Shader::AsMetaTag() const
{
	Tag *root = new Tag("Shader");
	if	(!root)
		__ERR__(__LOG_E__ << "Could not serialize shader. Failed to create root tag.\n", NULL)

	if	(input_list.GetCount())
		if	(Tag *it = root->AddChild("Input"))
			ListForeachPtr(ShaderInput *, input, input_list)
			{
				Tag *t = NULL;
				switch (input->type)
				{
					case ShaderInput::Attribute:	t = new Tag("Attribute");	break;
					case ShaderInput::Uniform:		t = new Tag("Uniform");		break;
				}
				if	(!t)
					continue;

				t->AddChild("Name", input->name);
				t->AddChild("Semantic", ShaderInput::semantic_desc[input->semantic].name);

				if	(Tag *st = t->AddChild("Scope"))
				{
					if	(input->scope & ShaderInput::Vertex)
						st->AddChild("Vertex");
					if	(input->scope & ShaderInput::Pixel)
						st->AddChild("Pixel");
					if	(input->scope & ShaderInput::Geometry)
						st->AddChild("Geometry");
				}

				switch (input->semantic)
				{
					case ShaderInput::Constant:
						switch (input->data_type)
						{
							case ShaderInput::Float:	t->AddChild("Type", "float");	break;
							case ShaderInput::Vector2:	t->AddChild("Type", "vec2");	break;
							case ShaderInput::Vector3:	t->AddChild("Type", "vec3");	break;
							case ShaderInput::Vector4:	t->AddChild("Type", "vec4");	break;
							case ShaderInput::Matrix3:	t->AddChild("Type", "mat3");	break;
							case ShaderInput::Matrix4:	t->AddChild("Type", "mat4");	break;
						}

						t->AddChild(input->parm_v.AsMetaTag("Vector"));
						break;
				}

				if	(!input->parm_t.IsEmpty())
					t->AddChild("Texture", input->parm_t.c_str());

				it->AddChild(t);
			}

	if	(varying_list.GetCount())
		if	(Tag *it = root->AddChild("Varying"))
			ListForeachPtr(ShaderVarying *, varying, varying_list)
				if	(Tag *v = it->AddChild("Variable"))
				{
					v->AddChild("Name", varying->name);
					v->AddChild("Type", varying->type);
				}

	if	(!geometry_decl.IsEmpty())
		root->AddChild("GeometryDeclaration", geometry_decl);
	if	(!vertex_decl.IsEmpty())
		root->AddChild("VertexDeclaration", vertex_decl);
	if	(!pixel_decl.IsEmpty())
		root->AddChild("PixelDeclaration", pixel_decl);

	if	(!geometry.IsEmpty())
		root->AddChild("GeometrySource", geometry);
	if	(!vertex.IsEmpty())
		root->AddChild("VertexSource", vertex);
	if	(!pixel.IsEmpty())
		root->AddChild("PixelSource", pixel);

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