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


#ifndef	__NSHADER_BLOCK__
#define	__NSHADER_BLOCK__


		#include "core/shader.h"
		#include "color/color.h"
		#include "math/matrix3.h"
		#include "math/matrix4.h"
		#include "nstring/nstring.h"


namespace GS {
	namespace NML { class Tag; }

	namespace Core {
		struct ShaderBlockPin;
		struct ShaderBlock;

typedef	ShaderBlock * pShaderBlock;

/*!
	@short	Block shader value.
	Used by the raytracer software implementation of the shader blocks.
*/
struct	ShaderBlockValue
{
	enum BlockValueType
	{
		BlockValueNone		=	0,
		BlockValueVector,
		BlockValueMatrix3,
		BlockValueMatrix4,
		BlockValueTexture
	};

	BlockValueType type;

	// Can't union those and most of all we don't want runtime allocations here...
	Vector4		v;
	Matrix3		m3;
	Matrix4		m4;
	String		t;

	void		Set(const float &_f)
	{	v.x = _f; type = BlockValueVector;	}
	void		Set(const Vector4 &_v)
	{	v = _v;	type = BlockValueVector;	}
	void		Set(const Matrix3 &_m)
	{	m3 = _m; type = BlockValueMatrix3;	}
	void		Set(const Matrix4 &_m)
	{	m4 = _m; type = BlockValueMatrix4;	}

	void		Set(const char *_t)
	{	t = _t; type = BlockValueTexture;	}

	ShaderBlockValue() : type(BlockValueNone) {}
};

/*!
	@short	Base render block.

	A render block is a component of a render map. It represents an input, an
	operator to the	render map.

	@author	Emmanuel Julien (ejulien@gsworks.fr)
*/
struct	ShaderBlock
{
enum	BlockType
{
		TypeNone	=	0,

		// Input blocks.
		TypeGeometryVertex,
		TypeGeometryNormal,
		TypeGeometryUV,
		TypeGeometrySkinning,
		TypeGeometryVertexColor,
		TypeGeometryTangentFrame,
		TypeRenderBuffer,
		TypeTexture,
		TypeTextureSampler,
		TypeConstant,
		TypeColor,
		TypeMaterialParam,
		TypeMaterialTexture,
		TypeScreenUV,
		TypeViewVector,
		TypeViewport,

		// Matrix blocks.
		TypeNormalViewMatrix,
		TypeNormalMatrix,
		TypeModelViewMatrix,
		TypeModelMatrix,

		// Operator blocks.
		TypeMix,
		TypeAdd,
		TypeMul,
		TypeSub,
		TypeDiv,
		TypeDot,
		TypeCross,
		TypeClamp,
		TypeNormalize,
		TypeSwizzle,
		TypeBuild,
		TypeSin,
		TypeCos,
		TypePow,
		TypeAbs,

		// Packing operators.
		TypeUnpackColorToVector,
		TypePackVectorToColor,

		// Engine parameters.
		TypeClock,

		TypeInvalid
};

		/// Verbose block type.
static	const char		*BlockTypeToString(BlockType type)
		{
			switch (type)
			{
				case TypeNone:					return "None";
					 
				case TypeGeometryVertex:		return "Geometry Vertex";
				case TypeGeometryNormal:		return "Geometry Normal";
				case TypeGeometryUV:			return "Geometry UV";
				case TypeGeometrySkinning:		return "Geometry Skinning";
				case TypeGeometryVertexColor:	return "Geometry RGB";
				case TypeGeometryTangentFrame:	return "Geometry Tangent Frame";
				case TypeRenderBuffer:			return "Render Buffer";
				case TypeTexture:				return "Texture";
				case TypeTextureSampler:		return "Texture Sampler";
				case TypeConstant:				return "Constant";
				case TypeColor:					return "Color";
				case TypeMaterialParam:			return "Material Parameter";
				case TypeMaterialTexture:		return "Material Texture";
				case TypeScreenUV:				return "Screen UV";
				case TypeViewVector:			return "View Vector";
				case TypeViewport:				return "Viewport";
					 
				case TypeNormalViewMatrix:		return "Normal View Matrix";
				case TypeNormalMatrix:			return "Normal Matrix ";	///< ! The additional space is NEEDED! (Normal matrix is the name of a legacy version of this node)
				case TypeModelViewMatrix:		return "Model View Matrix";
				case TypeModelMatrix:			return "Model Matrix";
					 
				case TypeMix:					return "Mix";
				case TypeAdd:					return "Add";
				case TypeMul:					return "Multiply";
				case TypeSub:					return "Subtract";
				case TypeDiv:					return "Divide";
				case TypeDot:					return "Dot";
				case TypeCross:					return "Cross";
				case TypeClamp:					return "Clamp";
				case TypeNormalize:				return "Normalize";
				case TypeSwizzle:				return "Swizzle";
				case TypeBuild:					return "Build";
				case TypeSin:					return "Sinus";
				case TypeCos:					return "Cosinus";
				case TypePow:					return "Pow";
				case TypeAbs:					return "Abs";
					 
				case TypeUnpackColorToVector:	return "Unpack color to vector";
				case TypePackVectorToColor:		return "Pack vector to color";
					 
				case TypeClock:					return "Clock";
					 
				case TypeInvalid:				break;
			}
			return "Invalid";
		}

		Vector2		pos;
		BlockType		type;

/*
	@name	Block input/output.
	@{
*/
		/// Get input count.
virtual	uint			GetInputCount() const = 0;
		/// Get input block.
virtual	ShaderBlock		*GetInput(uint n) const = 0;
		/// Set input.
virtual	bool			SetInput(uint n, ShaderBlock *in) = 0;

		/// Get input pin.
virtual	const ShaderBlockPin *GetInputPin(uint n) const = 0;
		/// Get block output pin.
virtual	const ShaderBlockPin *GetOutputPin() const = 0;

		/// Evaluate block output type.
virtual	int				GetOutputType() const = 0;
/// @}

		/// Get block label.
virtual	String			GetLabel() const = 0;
		/// Get block id.
virtual	String			GetId() const = 0;

		/// Is this block equivalent to another block.
		bool			IsEquivalent(const ShaderBlock *input_block) const;

		/// Gather all of this block children.
		void			GatherChildren(List <ShaderBlock *> &children);

		/// Import block map from metatag.
static	Array <pShaderBlock> *BlockMapFromMetaTag(NML::Tag &);
		/// Import branch from metatag.
static	ShaderBlock		*BranchFromMetaTag(NML::Tag &, Array <pShaderBlock> *block_map = 0);

static	NML::Tag		*BlockMapAsMetaTag(const List <ShaderBlock *> &block_map);
		NML::Tag		*BranchAsMetaTag(List <ShaderBlock *> &block_map);
		NML::Tag		*ParamAsMetaTag() const;

		ShaderBlock() : type(TypeNone) {}
virtual	~ShaderBlock() {}
};

//------------------------------------------------------------------------------
#define		__SpecializeBlock(__LABEL__, __INPUT_COUNT__)\
static	ShaderBlockPin	input_pin[__INPUT_COUNT__];\
static	ShaderBlockPin	output_pin;\
		\
		ShaderBlock		*input[__INPUT_COUNT__];\
		\
		String			GetLabel() const\
		{	return __LABEL__;	}\
		uint			GetInputCount() const\
		{	return __INPUT_COUNT__;	}\
		bool			SetInput(uint n, ShaderBlock *block)\
		{\
			if	(n >= __INPUT_COUNT__)\
				return false;\
			if	(block && !(input_pin[n].compatibility & block->GetOutputType()))\
				return false;\
			input[n] = block;\
			return true;\
		}\
		ShaderBlock	*GetInput(uint n) const\
		{	return (n < __INPUT_COUNT__) ? input[n] : 0;	}\
		const ShaderBlockPin *GetInputPin(uint n) const\
		{	return (n < __INPUT_COUNT__) ? &input_pin[n] : 0;	}\
		const ShaderBlockPin *GetOutputPin() const\
		{	return &output_pin;	}\
		int				GetOutputType() const;

#define		__SpecializeBlockNoInput(__LABEL__)\
static	ShaderBlockPin	output_pin;\
		\
		String			GetLabel() const\
		{	return __LABEL__;	}\
		uint			GetInputCount() const\
		{	return 0;	}\
		bool			SetInput(uint /*n*/, ShaderBlock */*block*/)\
		{	return false;	}\
		ShaderBlock		*GetInput(uint /*n*/) const\
		{	return 0;	}\
		const ShaderBlockPin *GetInputPin(uint /*n*/) const\
		{	return 0;	}\
		const ShaderBlockPin *GetOutputPin() const\
		{	return &output_pin;	}\
		int				GetOutputType() const;

//------------------------------------------------------------------------------

/*
	@short	Render block pin description.
	@author	Emmanuel Julien (ejulien@gsworks.fr)
*/
struct	ShaderBlockPin
{
		const char		*name,
						*desc;
		int				compatibility;		///< Pin connection compatibility
};

struct	SinusShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Sinus", 1)

		String			GetId() const
		{	return "Sin";	}
		SinusShaderBlock(ShaderBlock *block = 0)
		{
			type = TypeSin;
			input[0] = block;
		}
};

struct	CosinusShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Cosinus", 1)

		String			GetId() const
		{	return "Cos";	}
		CosinusShaderBlock(ShaderBlock *block = 0)
		{
			type = TypeCos;
			input[0] = block;
		}
};

struct	PowShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Pow", 2)

		String			GetId() const
		{	return "Pow";	}
		PowShaderBlock(ShaderBlock *v = 0, ShaderBlock *p = 0)
		{
			type = TypePow;
			input[0] = v;
			input[1] = p;
		}
};

struct	AbsShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Abs", 1)

		String			GetId() const
		{	return "Abs";	}
		AbsShaderBlock(ShaderBlock *v = 0)
		{
			type = TypeAbs;
			input[0] = v;
		}
};

struct	ClockShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Clock")

		String			GetId() const
		{	return "Clock";	}
		ClockShaderBlock()
		{	type = TypeClock;	}
};

struct	ScreenUVShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Screen UV")

		String			GetId() const
		{	return "ScreenUV";	}
		ScreenUVShaderBlock()
		{	type = TypeScreenUV;	}
};

struct	ViewVectorShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("View Vector")

		String			GetId() const
		{	return "ViewV";	}
		ViewVectorShaderBlock()
		{	type = TypeViewVector;	}
};

struct	ViewportShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Viewport")

		String			GetId() const
		{	return "Viewport";	}
		ViewportShaderBlock()
		{	type = TypeViewport;	}
};

struct	NormalViewMatrixShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Normal View Matrix")

		String			GetId() const
		{	return "NormalViewMatrix";	}
		NormalViewMatrixShaderBlock()
		{	type = TypeNormalViewMatrix;	}
};

struct	NormalMatrixShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Normal Matrix ")

		String			GetId() const
		{	return "NormalMatrix";	}
		NormalMatrixShaderBlock()
		{	type = TypeNormalMatrix;	}
};

struct	ModelViewMatrixShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Model View Matrix ")

		String			GetId() const
		{	return "ModelViewMatrix";	}
		ModelViewMatrixShaderBlock()
		{	type = TypeModelViewMatrix;	}
};

struct	ModelMatrixShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Model Matrix")

		String			GetId() const
		{	return "ModelMatrix";	}
		ModelMatrixShaderBlock()
		{	type = TypeModelMatrix;	}
};

struct	UnpackColorToVectorShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Unpack Color", 1)

		String			GetId() const
		{	return "UnpackColor";	}
		UnpackColorToVectorShaderBlock(ShaderBlock *block = 0)
		{
			type = TypeUnpackColorToVector;
			input[0] = block;
		}
};

struct	PackVectorToColorShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Pack Vector", 1)

		String			GetId() const
		{	return "PackVector";	}
		PackVectorToColorShaderBlock(ShaderBlock *block = 0)
		{
			type = TypePackVectorToColor;
			input[0] = block;
		}
};

struct	MixOperatorShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Mix", 3)

		String			GetId() const
		{	return "Mix";	}
		MixOperatorShaderBlock(ShaderBlock *a = 0, ShaderBlock *b = 0, ShaderBlock *k = 0)
		{
			type = TypeMix;
			input[0] = a; input[1] = b; input[2] = k;
		}
};

struct	AddOperatorShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Add", 2)

		String			GetId() const
		{	return "Add";	}
		AddOperatorShaderBlock(ShaderBlock *a = 0, ShaderBlock *b = 0)
		{
			type = TypeAdd;
			input[0] = a; input[1] = b;
		}
};

struct	MulOperatorShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Mul", 2)

		String			GetId() const
		{	return "Mul";	}
		MulOperatorShaderBlock(ShaderBlock *a = 0, ShaderBlock *b = 0)
		{
			type = TypeMul;
			input[0] = a; input[1] = b;
		}
};

struct	SubOperatorShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Sub", 2)

		String			GetId() const
		{	return "Sub";	}
		SubOperatorShaderBlock(ShaderBlock *a = 0, ShaderBlock *b = 0)
		{
			type = TypeSub;
			input[0] = a; input[1] = b;
		}
};

struct	DivOperatorShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Div", 2)

		String			GetId() const
		{	return "Div";	}
		DivOperatorShaderBlock(ShaderBlock *a = 0, ShaderBlock *b = 0)
		{
			type = TypeDiv;
			input[0] = a; input[1] = b;
		}
};

struct	DotOperatorShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Dot", 2)

		String			GetId() const
		{	return "Dot";	}
		DotOperatorShaderBlock(ShaderBlock *a = 0, ShaderBlock *b = 0)
		{
			type = TypeDot;
			input[0] = a; input[1] = b;
		}
};

struct	CrossOperatorShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Cross", 2)

		String			GetId() const
		{	return "Cross";	}
		CrossOperatorShaderBlock(ShaderBlock *a = 0, ShaderBlock *b = 0)
		{
			type = TypeCross;
			input[0] = a; input[1] = b;
		}
};

struct	NormalizeOperatorShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Normalize", 1)

		String			GetId() const
		{	return "Norm";	}
		NormalizeOperatorShaderBlock(ShaderBlock *a = 0)
		{
			type = TypeNormalize;
			input[0] = a;
		}
};

struct	ClampShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Clamp", 3)

		String			GetId() const
		{	return "Clamp";	}
		ClampShaderBlock(ShaderBlock *v = 0, ShaderBlock *a = 0, ShaderBlock *b = 0)
		{
			type = TypeClamp;
			input[0] = v; input[1] = a; input[2] = b;
		}
};

struct	SwizzleShaderBlock : public ShaderBlock
{
enum	Swizzle
{
	SwizzleNone	= 0,
	SwizzleX,
	SwizzleY,
	SwizzleZ,
	SwizzleW
};

		__SpecializeBlock("Swizzle", 1)

		String			GetId() const
		{	return String::Format("Swizzle%d%d%d%d", swizzle[0], swizzle[1], swizzle[2], swizzle[3]);	}
		Swizzle			swizzle[4];
		SwizzleShaderBlock(ShaderBlock *block = 0, Swizzle x = SwizzleNone, Swizzle y = SwizzleNone, Swizzle z = SwizzleNone, Swizzle w = SwizzleNone)
		{
			type = TypeSwizzle;
			swizzle[0] = x; swizzle[1] = y; swizzle[2] = z; swizzle[3] = w;
			input[0] = block;
		}
};

struct	BuildShaderBlock : public ShaderBlock
{
enum	Build
{
	BuildZero	= 0,
	BuildOne,
	BuildX,
	BuildY,
	BuildZ,
	BuildW
};

		__SpecializeBlock("Build", 4)

		Build			build[4];
		String			GetId() const
		{	return String::Format("Build%d%d%d%d", build[0], build[1], build[2], build[3]);	}
		BuildShaderBlock(ShaderBlock *a = 0, Build x = BuildX, ShaderBlock *b = 0, Build y = BuildY, ShaderBlock *c = 0, Build z = BuildZ, ShaderBlock *d = 0, Build w = BuildW)
		{
			type = TypeBuild;
			build[0] = x; build[1] = y; build[2] = z; build[3] = w;
			input[0] = a; input[1] = b; input[2] = c; input[3] = d;
		}
};

struct	GeometryUVShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("UV Stream")

		int				channel;
		String			GetId() const
		{	return String::Format("UV%d", channel);	}

		GeometryUVShaderBlock(int uv_channel = 0)
		{
			type = TypeGeometryUV;
			channel = uv_channel;
		}
};

struct  GeometryVertexShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Vertex Stream")

		String			GetId() const
		{	return String("Vertex");	}

		GeometryVertexShaderBlock()
		{	type = TypeGeometryVertex;	}
};

struct	GeometryNormalShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Normal Stream")

		bool			smooth;
		String			GetId() const
		{	return String(smooth ? "NormalSmooth" : "Normal");	}

		GeometryNormalShaderBlock()
		{
			type = TypeGeometryNormal;
			smooth = true;
		}
};

struct	GeometrySkinningShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Skinning Matrix")

		String			GetId() const
		{	return String("SkinMtx");	}

		GeometrySkinningShaderBlock()
		{	type = TypeGeometrySkinning;	}
};

struct	GeometryVertexColorShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Vertex Color Stream")

		String			GetId() const
		{	return String("VertexColor");	}
		GeometryVertexColorShaderBlock()
		{	type = TypeGeometryVertexColor;	}
};

struct	GeometryTangentFrameShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Tangent Frame")

		String			GetId() const
		{	return String("TangentMtx");	}

		GeometryTangentFrameShaderBlock()
		{	type = TypeGeometryTangentFrame;	}
};

struct	RenderBufferShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Render Buffer")

enum	RenderBuffer
{
	Depth	=	0
};

		RenderBuffer	buffer;
		String			GetId() const
		{	return String("RenderBuffer");	}

		RenderBufferShaderBlock(RenderBuffer _buffer = Depth)
		{
			type = TypeRenderBuffer;
			buffer = _buffer;
		}
};

struct	TextureShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Texture")

		enum TextureType
		{
			Texture2D	=	0,
			Texture3D,
			TextureCube
		};
		TextureType		texture_type;

		String			texture;
		String			GetId() const;

		TextureShaderBlock(const char *name = 0)
		{
			type = TypeTexture;
			texture = name;
			texture_type = Texture2D;
		}
};

struct	TextureSamplerShaderBlock : public ShaderBlock
{
		__SpecializeBlock("Texture Sampler", 2)

		enum SamplerType
		{
			Sampler2D	=	0,
			Sampler3D,
			SamplerCube
		};
		SamplerType		sampler_type;

		String			GetId() const
		{	return String::Format("TexSampler%d", (int)sampler_type);	}

		TextureSamplerShaderBlock(ShaderBlock *texture = 0, ShaderBlock *uv = 0)
		{
			type = TypeTextureSampler;

			sampler_type = Sampler2D;
			input[0] = texture;
			input[1] = uv;
		}
};

struct	MaterialParamShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Material Parameter")

enum	MaterialParam
{
	MaterialDiffuse	= 0,
	MaterialSpecular,
	MaterialSelf,
	MaterialAmbient,
	MaterialGlossiness,
	MaterialOpacity,
	MaterialReflection
};

		MaterialParam	param;

		String			GetId() const;
		MaterialParamShaderBlock(MaterialParam p = MaterialDiffuse)
		{
			type = TypeMaterialParam;
			param = p;
		}
};

struct	MaterialTextureShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Material Texture")

		enum TextureType
		{
			Texture2D	=	0,
			Texture3D,
			TextureCube
		};
		TextureType		texture_type;
		int				slot;

		String			GetId() const;

		MaterialTextureShaderBlock(int n = 0)
		{
			type = TypeMaterialTexture;
			texture_type = Texture2D;
			slot = n;
		}
};

struct	ColorShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Color")

		Color			color;

		String			GetId() const
		{	return "Color";	}

		ColorShaderBlock(float r = 1, float g = 1, float b = 1, float a = 1)
		{
			type = TypeColor;
			color.Set(r, g, b, a);
		}
};

struct	ConstantShaderBlock : public ShaderBlock
{
		__SpecializeBlockNoInput("Constant")

		float			constant[4];
		ShaderInput::DataType constant_type;
		String			GetId() const;

		ConstantShaderBlock()
		{
			type = TypeConstant; constant_type = ShaderInput::NoData;
			constant[0] = constant[1] = constant[2] = constant[3] = 0;
		}
		ConstantShaderBlock(float v)
		{
			type = TypeConstant; constant_type = ShaderInput::Float;
			constant[0] = v; constant[1] = constant[2] = constant[3] = 0;
		}
		ConstantShaderBlock(float u, float v)
		{
			type = TypeConstant; constant_type = ShaderInput::Vector2;
			constant[0] = u; constant[1] = v; constant[2] = constant[3] = 0;
		}
		ConstantShaderBlock(float x, float y, float z)
		{
			type = TypeConstant; constant_type = ShaderInput::Vector3;
			constant[0] = x; constant[1] = y; constant[2] = z; constant[3] = 0;
		}
		ConstantShaderBlock(float x, float y, float z, float w)
		{
			type = TypeConstant; constant_type = ShaderInput::Vector4;
			constant[0] = x; constant[1] = y; constant[2] = z; constant[3] = w;
		}
};

	} // Core
} // GS


#endif	// __NSHADER_BLOCK__
