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


		#include "core/material_to_shader_tree.h"
		#include "core/shader_block.h"
		#include "core/shader_tree.h"
		#include "log/log.h"

		using namespace GS;
		using namespace GS::Core;


//------------------------------------------------------------------------------
ShaderBlock			*MaterialToShaderTree::MaterialTextureStageToShaderBlock(const Material &m, const Material::TextureStage *ts, ShaderBlock *nb)
{
	if	(!ts)
		__ERR__(__LOG_W__ << "Cannot convert material null texture stage.\n", NULL)

	// Get texture stage index.
	uint n = 0;
	for (; n < Core::Material::max_texture_stage; ++n)
		if	(ts == &m.texstage[n])
			break;
	if	(n == Material::max_texture_stage)
		__ERR__(__LOG_W__ << "Texture stage does not belong to this material.\n", NULL)

	//
	MaterialTextureShaderBlock *texture_block = new MaterialTextureShaderBlock(n);
	TextureSamplerShaderBlock *sampler_block = new TextureSamplerShaderBlock;
	ShaderBlock *uv_block = NULL, *output_block = NULL;

	switch (ts->uv_mode)
	{
		case Material::UV_UV:
			uv_block = new GeometryUVShaderBlock(ts->uv_index);
			break;

		case Material::UV_FrontMap:
		{
			uv_block = new DivOperatorShaderBlock(new ScreenUVShaderBlock, new SwizzleShaderBlock(new ViewportShaderBlock, SwizzleShaderBlock::SwizzleZ, SwizzleShaderBlock::SwizzleW));
			uv_block = new MulOperatorShaderBlock(new ConstantShaderBlock(1, -1), uv_block);
			if	((ts->channel == Channel_Reflection) && nb)
			{
				ShaderBlock *offset_block = new MulOperatorShaderBlock(new SwizzleShaderBlock(nb, SwizzleShaderBlock::SwizzleX, SwizzleShaderBlock::SwizzleY), new ConstantShaderBlock(0.01f, 0.01f));
				uv_block = new AddOperatorShaderBlock(uv_block, offset_block);
			}
		}
		break;

		case Material::UV_LSN:
		{
			ShaderBlock *tmp = new MulOperatorShaderBlock(new NormalViewMatrixShaderBlock, nb ? nb : new GeometryNormalShaderBlock);
			tmp = new PackVectorToColorShaderBlock(tmp);
			tmp = new SwizzleShaderBlock(tmp, SwizzleShaderBlock::SwizzleX, SwizzleShaderBlock::SwizzleY);
			uv_block = tmp;
		}
		break;

		case Material::UV_SphericalEnvironment:
		{
			// find the reflective vector
			ConstantShaderBlock*			negative_vector = new ConstantShaderBlock(-1.0f, -1.0f, -1.0f);

			GeometryNormalShaderBlock*		normal_stream =  nb ? (GeometryNormalShaderBlock*)nb : new GeometryNormalShaderBlock;
			NormalMatrixShaderBlock*		normal_matrix = new NormalMatrixShaderBlock();
			MulOperatorShaderBlock*			mul_operator_normal = new MulOperatorShaderBlock(normal_matrix, normal_stream);

			MulOperatorShaderBlock*			mul_operator1 = new MulOperatorShaderBlock(negative_vector, mul_operator_normal);

			// dot the negative normal and the view vector
			ViewVectorShaderBlock*			view_vector	= new ViewVectorShaderBlock();
			DotOperatorShaderBlock*			dot_vector1 =new  DotOperatorShaderBlock(mul_operator1, view_vector);

			// take the abs of the dot
			AbsShaderBlock*					abs_operator2 = new AbsShaderBlock(dot_vector1);

			// normal * 2 * abs(dot(-n, view))
			ConstantShaderBlock*			constant_vector2 = new ConstantShaderBlock(2.0f, 2.0f, 2.0f);
			MulOperatorShaderBlock*			mul_operator3 = new MulOperatorShaderBlock(mul_operator_normal, constant_vector2);

			BuildShaderBlock*				build_vector1 = new BuildShaderBlock(abs_operator2, BuildShaderBlock::BuildX, abs_operator2, BuildShaderBlock::BuildX, abs_operator2, BuildShaderBlock::BuildX);

			MulOperatorShaderBlock*			mul_operator4 = new MulOperatorShaderBlock(build_vector1, mul_operator3);

			// view + (normal * 2 * abs(dot(-n, view)))
			AddOperatorShaderBlock*			add_vector1 = new AddOperatorShaderBlock(view_vector, mul_operator4);

			NormalizeOperatorShaderBlock*	normalize1 = new NormalizeOperatorShaderBlock(add_vector1);

			//compute the euler angle from the vector , to transform into uv coordinate

			// change the Y into {0,1}
			BuildShaderBlock*				build_vector2 = new BuildShaderBlock(normalize1, BuildShaderBlock::BuildY );
			PackVectorToColorShaderBlock*	pack_vector1 = new PackVectorToColorShaderBlock(build_vector2);

			// change the XZ into {0, 1}
			BuildShaderBlock*				build_vector3 = new BuildShaderBlock(normalize1, BuildShaderBlock::BuildX, normalize1, BuildShaderBlock::BuildZ, NULL, BuildShaderBlock::BuildZero);
			NormalizeOperatorShaderBlock*	normalize2 = new NormalizeOperatorShaderBlock(build_vector3 );

			// do the dot in x
			ConstantShaderBlock*			constant_vector5 = new ConstantShaderBlock(1.0f, 0.0f, 0.0f);
			DotOperatorShaderBlock*			dot_vector2 =new  DotOperatorShaderBlock(constant_vector5, normalize2);

			// set this dot from {-1, 1} to {0, 1}			
			PackVectorToColorShaderBlock*	pack_vector2 = new PackVectorToColorShaderBlock(dot_vector2);

			// do the dot in y
			ConstantShaderBlock*			constant_vector6 = new ConstantShaderBlock(0.0f, 1.0f, 0.0f);
			DotOperatorShaderBlock*			dot_vector3 =new  DotOperatorShaderBlock(constant_vector6, normalize2);

			// abs the dot and make it -1 or 1
			AbsShaderBlock*					abs_operator3 = new AbsShaderBlock(dot_vector3);
			DivOperatorShaderBlock*			div_vector3 = new DivOperatorShaderBlock(dot_vector3  , abs_operator3);

			// multiply the 2 transform dot to obtain the good angle between {-1, 1}
			MulOperatorShaderBlock*			mul_operator6 = new MulOperatorShaderBlock(pack_vector2, div_vector3);

			// transform result from {-1, 1} to {0, 1}
			PackVectorToColorShaderBlock*	pack_vector3 = new PackVectorToColorShaderBlock(mul_operator6);

			// create the vector for the sampler 2D
			BuildShaderBlock*				build_vector4 = new BuildShaderBlock(pack_vector1, BuildShaderBlock::BuildX, pack_vector3, BuildShaderBlock::BuildX);

			uv_block = build_vector4;
		}
		break;
	}

	sampler_block->input[0] = texture_block;
	sampler_block->input[1] = uv_block;

	output_block = sampler_block;
	switch (ts->channel)
	{
		case Channel_Normal:
			if	(m.renderword & Material::Render_NormalTangent)
			{
				GeometryTangentFrameShaderBlock *tangent_frame_block = new GeometryTangentFrameShaderBlock;
				SwizzleShaderBlock *swizzle_block = new SwizzleShaderBlock(sampler_block, SwizzleShaderBlock::SwizzleX, SwizzleShaderBlock::SwizzleY, SwizzleShaderBlock::SwizzleZ);
				output_block = new MulOperatorShaderBlock(tangent_frame_block, new UnpackColorToVectorShaderBlock(swizzle_block));
			}
			else
				output_block = new UnpackColorToVectorShaderBlock(new SwizzleShaderBlock(sampler_block, SwizzleShaderBlock::SwizzleX, SwizzleShaderBlock::SwizzleY, SwizzleShaderBlock::SwizzleZ));
			break;

		default: break;
	}
	return output_block;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				MaterialToShaderTree::Convert(const Material &m, ShaderTree &tree)
{
	// Normal.
	ShaderBlock *normal_block = NULL;
	if	(m.GetChannelStage(Channel_Normal))
			normal_block = new NormalizeOperatorShaderBlock(MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_Normal)));
	else	normal_block = new GeometryNormalShaderBlock;

	// Diffuse.
	ShaderBlock *diffuse_block = NULL;

	if	(m.GetChannelStage(Channel_Diffuse))
	{
		ShaderBlock *texa_block = MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_Diffuse), normal_block);

		if	(m.GetChannelStage(Channel_BlendRGB))
		{
			ShaderBlock *texb_block = MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_BlendRGB), normal_block);
			diffuse_block = new MixOperatorShaderBlock(texb_block, texa_block, new SwizzleShaderBlock(new GeometryVertexColorShaderBlock, SwizzleShaderBlock::SwizzleX));
		}
		else
			diffuse_block = texa_block;
	}
	else	diffuse_block = new MaterialParamShaderBlock(MaterialParamShaderBlock::MaterialDiffuse);

	// RGB stream (only if no blend rgb stage active).
	GeometryVertexColorShaderBlock *vertex_color_block = NULL;

	if	(!m.GetChannelStage(Channel_BlendRGB))
		if	(m.renderword & Material::Render_VertexColor)
		{
			vertex_color_block = new GeometryVertexColorShaderBlock;
			diffuse_block = new MulOperatorShaderBlock(diffuse_block, new BuildShaderBlock(vertex_color_block, BuildShaderBlock::BuildX, vertex_color_block, BuildShaderBlock::BuildY, vertex_color_block, BuildShaderBlock::BuildZ, 0, BuildShaderBlock::BuildOne));
		}

	// Light map & decal map.
	if	(m.GetChannelStage(Channel_Decal))
	{
		ShaderBlock *decal_block = MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_Decal), normal_block);
		diffuse_block = new MixOperatorShaderBlock(decal_block, diffuse_block, new SwizzleShaderBlock(decal_block, SwizzleShaderBlock::SwizzleW));
	}
	if	(m.GetChannelStage(Channel_Light))
		diffuse_block = new MulOperatorShaderBlock(diffuse_block, MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_Light), normal_block));

	// Self.
	ShaderBlock *constant_block = NULL;
	if	(m.GetChannelStage(Channel_SelfIllum))
			constant_block = MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_SelfIllum), normal_block);
	else	constant_block = new MaterialParamShaderBlock(MaterialParamShaderBlock::MaterialSelf);

	// Specular.
	ShaderBlock *specular_block = NULL;
	if	(m.GetChannelStage(Channel_Specular))
			specular_block = MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_Specular), normal_block);
	else	specular_block = new MaterialParamShaderBlock(MaterialParamShaderBlock::MaterialSpecular);

	// Reflection.
	if	(m.GetChannelStage(Channel_Reflection))
	{
		Material::TextureStage *reflection_stage = m.GetChannelStage(Channel_Reflection);
		ShaderBlock *reflection_block = MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, reflection_stage, normal_block);

		switch (reflection_stage->op)
		{
			case Material::Operator_Default:
			case Material::Operator_Add:
				constant_block = new AddOperatorShaderBlock(constant_block, reflection_block);
				break;

			case Material::Operator_Multiply:
				diffuse_block = new MulOperatorShaderBlock(diffuse_block, reflection_block);
				break;
		}
	}

	// Glossiness.
	ShaderBlock *glossiness_block = NULL;
	if	(m.GetChannelStage(Channel_Glossiness))
			glossiness_block = new SwizzleShaderBlock(MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_Glossiness), normal_block), SwizzleShaderBlock::SwizzleX);
	else	glossiness_block = new MaterialParamShaderBlock(MaterialParamShaderBlock::MaterialGlossiness);

	// Opacity.
	ShaderBlock *opacity_block = NULL;
	if	(m.GetChannelStage(Channel_Opacity))
			opacity_block = new SwizzleShaderBlock(MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_Opacity), normal_block), SwizzleShaderBlock::SwizzleW);
	else	opacity_block = new MaterialParamShaderBlock(MaterialParamShaderBlock::MaterialOpacity);

	if	(vertex_color_block)
		opacity_block = new MulOperatorShaderBlock(opacity_block, new SwizzleShaderBlock(vertex_color_block, SwizzleShaderBlock::SwizzleW));

	// Reflection (raytracer).
	ShaderBlock *reflection_raytracer_block = NULL;
	reflection_raytracer_block = new MaterialParamShaderBlock(MaterialParamShaderBlock::MaterialReflection);

	// Assign to sinks.
	tree.sink[ShaderTree::SinkDiffuse] = diffuse_block;
	tree.sink[ShaderTree::SinkSpecular] = specular_block;
	tree.sink[ShaderTree::SinkConstant] = constant_block;
	tree.sink[ShaderTree::SinkNormal] = normal_block;
	tree.sink[ShaderTree::SinkOpacity] = opacity_block;
	tree.sink[ShaderTree::SinkGlossiness] = glossiness_block;
	tree.sink[ShaderTree::SinkReflection] = reflection_raytracer_block;

	return true;
}
//------------------------------------------------------------------------------
