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


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

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


//------------------------------------------------------------------------------
bool				Shader::AddISLSection(const char *uri)
{
	File isl_file;
	if	(!Parser::Load(uri, isl_file))
		return false;

	// Declare inputs.
	ParseInputTag(isl_file.GetTag("Shader:Input;"));
	ParseVaryingTag(isl_file.GetTag("Shader:Varying;"));

	// Append vertex & fragment programs.
	bool r = true;
	if	(Tag *t = isl_file.GetTypedTag("Shader:Vertex;", Variant::VariantString))
	{
		Array <char> data;
		if	((r &= Platform::Get().io->FileLoad(t->GetString(), data)) != false)
			vertex += String(data.c_ptr(), data.GetSize());
	}
	if	(Tag *t = isl_file.GetTypedTag("Shader:Fragment;", Variant::VariantString))
	{
		Array <char> data;
		if	((r &= Platform::Get().io->FileLoad(t->GetString(), data)) != false)
			pixel += String(data.c_ptr(), data.GetSize());
	}
	return r;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Shader::Clone(Shader &clone) const
{
	// TODO make something faster... please.
	AutoPtr <Tag> tag(AsMetaTag());
	clone.name = name;
	return clone.FromMetaTag(*tag);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Shader::Define(const char *def, ShaderInput::Scope scope)
{
	String directive = String::Format("#define %s\n", def);

	if	(scope & ShaderInput::Vertex)
		vertex_decl = directive + vertex_decl;
	if	(scope & ShaderInput::Pixel)
		pixel_decl = directive + pixel_decl;
}
ShaderVarying		*Shader::DeclareVarying(const char *name, const char *type)
{
	ShaderVarying *varying = new ShaderVarying;
	if	(!varying)
		return NULL;

	varying_list.Add(varying);

	varying->name = name;
	varying->type = type;
	return varying;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
ShaderInput			*Shader::GetInput(ShaderInput::Semantic semantic) const
{
	ListForeachPtr(ShaderInput *, input, input_list)
		if	(input->semantic == semantic)
			return input;
	return NULL;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
ShaderInput			*Shader::DeclareInput(const char *name, ShaderInput::DataType data_type, ShaderInput::Semantic semantic, ShaderInput::Type parm_type, ShaderInput::Scope parm_scope, uint array_size)
{
	// Catch duplicates.
	ShaderInput *input = NULL;

	switch (semantic)	
	{
		case ShaderInput::Position:
		case ShaderInput::Normal:
		case ShaderInput::UV0:
		case ShaderInput::UV1:
		case ShaderInput::UV2:
		case ShaderInput::Tangent:
		case ShaderInput::Bitangent:
		case ShaderInput::BoneIndex:
		case ShaderInput::BoneWeight:
			ListForeachPtr(ShaderInput *, p, input_list)
				if	(p->semantic == semantic)
				{
					input = p;
					break;
				}
			break;

		default:
			ListForeachPtr(ShaderInput *, p, input_list)
				if	((p->name == name) && (p->semantic == semantic))
				{
					input = p;
					break;
				}
			break;
	}

	// Equivalent input found.
	if	(input)
	{
		input->scope |= parm_scope;		// Merge scopes.
		return input;
	}

	// Create a new input.
	if	((input = new ShaderInput) == NULL)
		return NULL;
	input_list.Add(input);

	input->name = name;
	input->semantic = semantic;
	input->type = parm_type;
	input->data_type = data_type;
	input->scope = parm_scope;
	input->array_size = array_size;

	input->parm_v.Set(0, 0, 0);

	return input;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Shader::Clear()
{
	input_list.Clear();
	varying_list.Clear();

	geometry_decl.Clear();
	vertex_decl.Clear();
	pixel_decl.Clear();
	geometry.Clear();
	vertex.Clear();
	pixel.Clear();
}
//------------------------------------------------------------------------------

Shader::Shader(const char *n, const char *v, const char *f, const char *g)
{
	name = n;
	vertex = v;
	pixel = f;
	geometry = g;
}
