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


		#include "core/shader_tree_compiler.h"
		#include "core/geometry.h"
		#include "log/log.h"

		using namespace GS;
		using namespace GS::Core;


//------------------------------------------------------------------------------
bool				ShaderTreeCompiler::GetNewVariable(String &variable, const char *prefix)
{
	variable = String::Format("n_%s%d", prefix ? prefix : "n_var", variable_count++);
	return true;
}
CShaderBlock		*ShaderTreeCompiler::GetNewCompiledBlock(const ShaderBlock *block)
{
	List <CShaderBlock *> ::Item *item = compiled_tree_block.Add(new CShaderBlock(block));
	return item ? item->Object() : NULL;
}
//------------------------------------------------------------------------------

//-------------------------------------------------------------------
void				ShaderTreeCompiler::AddPrefix(const char *prefix)
//-------------------------------------------------------------------
{
	if	(prefix)
		id += prefix;
}

//-------------------------------------------------------------------------------------------------------------------------
CShaderBlock		*ShaderTreeCompiler::CompileTemporary(ShaderBlock *block, const char *prefix, ShaderInput::Scope scope)
//-------------------------------------------------------------------------------------------------------------------------
{
	temporary_block_list.Add(block);
	return CompileShaderBlock(block, prefix, scope);
}

//---------------------------------------------------------------------------------------------------------------------------------
CShaderBlock		*ShaderTreeCompiler::CompileShaderBlock(const ShaderBlock *block, const char *prefix, ShaderInput::Scope scope)
//---------------------------------------------------------------------------------------------------------------------------------
{
	if	(!block)
		return NULL;

	// Id should describe the full tree structure.
	AddPrefix(prefix);
	id += block->GetId();

	/*
		Avoid compiling twice a block referenced multiple times.
		Also geometry input blocks only need to be evaluated once.
	*/
	ListForeachPtr(CShaderBlock *, compiled_block, compiled_tree_block)
		if	(compiled_block->block->IsEquivalent(block))
			return compiled_block;

	// Compile as new block.
	switch (block->type)
	{
		case ShaderBlock::TypeConstant:
			return CompileConstantShaderBlock((const ConstantShaderBlock *)block, scope);
		case ShaderBlock::TypeColor:
			return CompileColorShaderBlock((const ColorShaderBlock *)block, scope);
		case ShaderBlock::TypeMaterialParam:
			return CompileMaterialParamShaderBlock((const MaterialParamShaderBlock *)block, scope);
		case ShaderBlock::TypeMaterialTexture:
			return CompileMaterialTextureShaderBlock((const MaterialTextureShaderBlock *)block, scope);

		case ShaderBlock::TypeGeometryVertex:
			return CompileGeometryVertexShaderBlock((const GeometryVertexShaderBlock *)block, scope);
		case ShaderBlock::TypeGeometryNormal:
			return CompileGeometryNormalShaderBlock((const GeometryNormalShaderBlock *)block, scope);
		case ShaderBlock::TypeGeometrySkinning:
			return CompileGeometrySkinningShaderBlock((const GeometrySkinningShaderBlock *)block, scope);
		case ShaderBlock::TypeGeometryVertexColor:
			return CompileGeometryVertexColorShaderBlock((const GeometryVertexColorShaderBlock *)block, scope);
		case ShaderBlock::TypeGeometryTangentFrame:
			return CompileGeometryTangentFrameShaderBlock((const GeometryTangentFrameShaderBlock *)block, scope);
		case ShaderBlock::TypeGeometryUV:
			return CompileGeometryUVShaderBlock((const GeometryUVShaderBlock *)block, scope);
		case ShaderBlock::TypeRenderBuffer:
			return CompileRenderBufferShaderBlock((const RenderBufferShaderBlock *)block, scope);
		case ShaderBlock::TypeTexture:
			return CompileTextureShaderBlock((const TextureShaderBlock *)block, scope);
		case ShaderBlock::TypeTextureSampler:
			return CompileTextureSamplerShaderBlock((const TextureSamplerShaderBlock *)block, scope);

		case ShaderBlock::TypeMix:
			return CompileMixShaderBlock((const MixOperatorShaderBlock *)block, scope);
		case ShaderBlock::TypeAdd:
			return CompileAddShaderBlock((const AddOperatorShaderBlock *)block, scope);
		case ShaderBlock::TypeSub:
			return CompileSubShaderBlock((const SubOperatorShaderBlock *)block, scope);
		case ShaderBlock::TypeMul:
			return CompileMulShaderBlock((const MulOperatorShaderBlock *)block, scope);
		case ShaderBlock::TypeDiv:
			return CompileDivShaderBlock((const DivOperatorShaderBlock *)block, scope);
		case ShaderBlock::TypeSwizzle:
			return CompileSwizzleShaderBlock((const SwizzleShaderBlock *)block, scope);
		case ShaderBlock::TypeBuild:
			return CompileBuildShaderBlock((const BuildShaderBlock *)block, scope);
		case ShaderBlock::TypeCos:
			return CompileCosinusShaderBlock((const CosinusShaderBlock *)block, scope);
		case ShaderBlock::TypeSin:
			return CompileSinusShaderBlock((const SinusShaderBlock *)block, scope);
		case ShaderBlock::TypePow:
			return CompilePowShaderBlock((const PowShaderBlock *)block, scope);
		case ShaderBlock::TypeAbs:
			return CompileAbsShaderBlock((const AbsShaderBlock *)block, scope);

		case ShaderBlock::TypeNormalize:
			return CompileNormalizeBlock((const NormalizeOperatorShaderBlock *)block, scope);
		case ShaderBlock::TypeDot:
			return CompileDotShaderBlock((const DotOperatorShaderBlock *)block, scope);
		case ShaderBlock::TypeCross:
			return CompileCrossShaderBlock((const CrossOperatorShaderBlock *)block, scope);
		case ShaderBlock::TypeClamp:
			return CompileClampShaderBlock((const ClampShaderBlock *)block, scope);

		case ShaderBlock::TypePackVectorToColor:
			return CompilePackVectorToColor((const PackVectorToColorShaderBlock *)block, scope);
		case ShaderBlock::TypeUnpackColorToVector:
			return CompileUnpackColorToVector((const UnpackColorToVectorShaderBlock *)block, scope);

		case ShaderBlock::TypeClock:
			return CompileClockShaderBlock((const ClockShaderBlock *)block, scope);
		case ShaderBlock::TypeScreenUV:
			return CompileScreenUVShaderBlock((const ScreenUVShaderBlock *)block, scope);
		case ShaderBlock::TypeViewVector:
			return CompileViewVectorShaderBlock((const ViewVectorShaderBlock *)block, scope);
		case ShaderBlock::TypeViewport:
			return CompileViewportShaderBlock((const ViewportShaderBlock *)block, scope);

		case ShaderBlock::TypeNormalViewMatrix:
			return CompileNormalViewMatrixShaderBlock((const NormalViewMatrixShaderBlock *)block, scope);
		case ShaderBlock::TypeNormalMatrix:
			return CompileNormalMatrixShaderBlock((const NormalMatrixShaderBlock *)block, scope);
		case ShaderBlock::TypeModelViewMatrix:
			return CompileModelViewMatrixShaderBlock((const ModelViewMatrixShaderBlock *)block, scope);
		case ShaderBlock::TypeModelMatrix:
			return CompileModelMatrixShaderBlock((const ModelMatrixShaderBlock *)block, scope);

		default:
			__LOG_E__ << "Unsupported render block type (" << block->type << "), does not known how to compile.\n";
			break;
	}
	return NULL;
}

//------------------------------------------------------------------------------
void				ShaderTreeCompiler::Free()
{
	id.Clear();

	compiled_tree_block.Clear();
	temporary_block_list.Clear();

	vertex_declaration.Clear();
	vertex_source.Clear();
	pixel_declaration.Clear();
	pixel_source.Clear();

	texture_count = 0;
	variable_count = 0;
	shader = NULL;
}
void				ShaderTreeCompiler::RestartCompiler(Shader *s)
{
	Free();
	shader = s;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
ShaderTreeCompiler::ShaderTreeCompiler()
{
	texture_count = 0;
	variable_count = 0;
}
ShaderTreeCompiler::~ShaderTreeCompiler()
{	Free();	}
//------------------------------------------------------------------------------
