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


		#include "core/shader_isl_to_glsl.h"
		#include "core/shader.h"
		#include "log/log.h"

		using namespace GS;
		using namespace GS::Core;


//------------------------------------------------------------------------------
bool				ISLtoGLSL::GetType(ShaderInput::DataType data_type, String &decl)
{
	switch (data_type)
	{
		case ShaderInput::Float:				decl = "float";				return true;
		case ShaderInput::Vector2:				decl = "vec2";				return true;
		case ShaderInput::Vector3:				decl = "vec3";				return true;
		case ShaderInput::Vector4:				decl = "vec4";				return true;
		case ShaderInput::Matrix3:				decl = "mat3";				return true;
		case ShaderInput::Matrix4:				decl = "mat4";				return true;
		case ShaderInput::DataTexture2D:		decl = "sampler2D";			return true;
		case ShaderInput::DataTexture3D:		decl = "sampler3D";			return true;
		case ShaderInput::DataTextureCube:		decl = "samplerCube";		return true;
		case ShaderInput::DataTextureShadow:	decl = "sampler2DShadow";	return true;
	}
	return false;
}
bool				ISLtoGLSL::Translate(const Shader &shader, String &glsl_vertex, String &glsl_pixel, GLSLVariant variant)
{
	glsl_vertex = shader.vertex;
	glsl_pixel = shader.pixel;

	String vertex_decl, pixel_decl, type_decl;

	// Variant defaults.
	switch (variant)
	{
		case EGL20:
		{
			vertex_decl += "#version 100\n\n";		// EGL 100 ~= GL 120
			pixel_decl += "#version 100\n\n";

			static String gles_precision("precision mediump float;\n");
			vertex_decl += gles_precision;
			pixel_decl += gles_precision;
		}
		break;

		case OGL32:
			vertex_decl += "#version 130\n\n";
			pixel_decl += "#version 130\n\n";
			break;

		default:
			// Allow implicit conversions.
//			vertex_decl += "#version 120\n\n";
//			pixel_decl += "#version 120\n\n";
			break;
	}

	// Helper macros.
	String	mtx_mul = "#define	n_mtx_mul(A, B)	((A)*(B))\n";
	vertex_decl += mtx_mul;
	pixel_decl += mtx_mul;

	String	mtx_conv = "mat3 _mat4_to_mat3(mat4 m) { return mat3(m[0].xyz, m[1].xyz, m[2].xyz); }\n";
	vertex_decl += mtx_conv;
	pixel_decl += mtx_conv;

	String buildm3 = "\nmat3 _build_mat3(vec3 a, vec3 b, vec3 c) { return mat3(a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z); }\n";
	vertex_decl += buildm3;
	pixel_decl += buildm3;

	// Convert symbols.
	{
		static const char *isl_symbol[] = { "%out.position%", NULL };
		static const char *egl_symbol[] = { "gl_Position", NULL };
		glsl_vertex.ReplaceAll(isl_symbol, egl_symbol, true);
	}
	{
		static const char *isl_symbol[] = { "%in.fragcoord%", "%out.color%", "%out.color0%", "%out.color1%", "%out.color2%", "%out.color3%", "%out.depth%", NULL };
		static const char *egl_symbol[] = { "gl_FragCoord", "gl_FragColor", "gl_FragData[0]", "gl_FragData[1]", "gl_FragData[2]", "gl_FragData[3]", "gl_FragDepth", NULL };
		glsl_pixel.ReplaceAll(isl_symbol, egl_symbol, true);
	}

	//
	{
		static const char *outputs[] =	{	"%position%", "%normal%", "%diffuse%", "%specular%", "%glossiness%", "%constant%", "%opacity%", NULL	};
		static const char *out_vars[] =	{	"_o_vertex", "_o_normal", "_o_diffuse", "_o_specular", "_o_glossiness", "_o_constant", "_o_opacity", NULL	};
		glsl_vertex.ReplaceAll(outputs, out_vars, true);
		glsl_pixel.ReplaceAll(outputs, out_vars, true);
	}

	// Create function declaration.
	if	(!glsl_vertex.Replace("%main%", "void main()"))
		glsl_vertex = String("void main()\n{\n") + glsl_vertex + "}";
	if	(!glsl_pixel.Replace("%main%", "void main()"))
		glsl_pixel = String("void main()\n{\n") + glsl_pixel + "}";

	// Declare inputs.
	ListForeachPtr(ShaderInput *, input, shader.input_list)
		if	(GetType(input->data_type, type_decl))
		{
			if	(variant == EGL20)
			{
				static String lowp("lowp "), mediump("mediump "), highp("highp ");

				switch (ShaderInput::semantic_desc[input->semantic].precision)
				{
					case ShaderInput::LowP:		type_decl = lowp + type_decl;		break;
					case ShaderInput::MediumP:	type_decl = mediump + type_decl;	break;
					case ShaderInput::HighP:	type_decl = highp + type_decl;		break;
				}
			}

			String	input_decl = input->array_size > 1 ? String::Format("%s[%d]", input->name.c_str(), input->array_size) : input->name,
					local_decl;

			switch (input->type)
			{
				case ShaderInput::Attribute:
					local_decl = String::Format("attribute %s %s;\n", type_decl.c_str(), input_decl.c_str());
					break;

				case ShaderInput::Uniform:
					local_decl = String::Format("uniform %s %s;\n", type_decl.c_str(), input_decl.c_str());
					break;
			}

			if	(input->scope & ShaderInput::Vertex)
				vertex_decl += local_decl;
			if	(input->scope & ShaderInput::Pixel)
				pixel_decl += local_decl;
		}
		else
			__LOG_E__ << "Unsupported input '" << input->name << "' type (" << input->type << ").\n";

	// Declare varyings.
	ListForeachPtr(ShaderVarying *, varying, shader.varying_list)
	{
		String	decl = String::Format("varying %s %s;\n", varying->type.c_str(), varying->name.c_str());
		vertex_decl += decl;
		pixel_decl += decl;
	}

	// Assemble final source.
	glsl_vertex = vertex_decl + shader.vertex_decl + glsl_vertex;
	glsl_pixel = pixel_decl + shader.pixel_decl + glsl_pixel;

	//
	if	(variant == EGL20)
	{
		static const char *isl_symbol[] = { "sampler2DShadow",	"shadow2D",		NULL };
		static const char *egl_symbol[] = { "sampler2D",		"texture2D",	NULL };
		glsl_vertex.ReplaceAll(isl_symbol, egl_symbol, true);
		glsl_pixel.ReplaceAll(isl_symbol, egl_symbol, true);
	}
	return true;
}
//------------------------------------------------------------------------------
