#ifndef FVF_hpp
#define FVF_hpp

#include "MetaMacro.hpp"
#include "Point.hpp"
#include "Vector.hpp"
#include "Color.hpp"
#include "Types.hpp"

namespace swShader
{
	enum
	{
		FVF_POSITION		= 0,	// Assumed always present
		FVF_RHW				= 1 << 0,
		FVF_POSITIONT		= FVF_RHW,
		FVF_NORMAL			= 1 << 1,
		FVF_POINT_SIZE		= 1 << 2,
		FVF_COLOR			= 1 << 3,
		FVF_DIFFUSE			= FVF_COLOR,
		FVF_LIGHT			= 1 << 4,
		FVF_SPECULAR		= FVF_LIGHT,

		FVF_TEX_MASK		= 0xFF << 5,
		FVF_TEX_SHIFT		= 5,
		FVF_TEXTURE			= 1 << 5,
		FVF_TEXTURE0		= FVF_TEXTURE,
		FVF_TEXTURE1		= 1 << 6  | FVF_TEXTURE0,
		FVF_TEXTURE2		= 1 << 7  | FVF_TEXTURE1,
		FVF_TEXTURE3		= 1 << 8  | FVF_TEXTURE2,
		FVF_TEXTURE4		= 1 << 9  | FVF_TEXTURE3,
		FVF_TEXTURE5		= 1 << 10 | FVF_TEXTURE4,
		FVF_TEXTURE6		= 1 << 11 | FVF_TEXTURE5,
		FVF_TEXTURE7		= 1 << 12 | FVF_TEXTURE6,

		// Only for internal use (vertex shader output)
		FVF_FOG				= 1 << 13,

		FVF_LAST			= FVF_FOG,

		// Predefined formats
		FVF_VERTEX			= FVF_NORMAL | FVF_TEXTURE,
		FVF_LVERTEX			= FVF_DIFFUSE | FVF_SPECULAR | FVF_TEXTURE,
		FVF_LTVERTEX		= FVF_RHW | FVF_DIFFUSE | FVF_SPECULAR | FVF_TEXTURE,
		FVF_XVERTEX			= FVF_RHW | FVF_DIFFUSE | FVF_SPECULAR | FVF_TEXTURE7
	};

	class FVFFlags
	{
	public:
		FVFFlags();
		FVFFlags(int flags);

		~FVFFlags();

		FVFFlags operator|(int flag) const;

		bool hasRHW() const;
		bool hasNormal() const;
		bool hasColor() const;
		bool hasDiffuse() const;
		bool hasLight() const;
		bool hasSpecular() const;
		bool hasTexture(int i) const;

		int textureCount() const;

	private:
		int flags;
	};

	struct UV
	{
		float u;
		float v;
	};

	ALIGN(16) struct UVW
	{
		UVW()
		{
		}

		UVW(const UV &uv)
		{
			u = uv.u;
			v = uv.v;
		}

		float u;
		float v;
		float w;
		float m;
	};

	template<unsigned int flags>
	class FVFGenerator
	{
		struct Position
		{
			union
			{
				struct
				{
					float x;
					float y;
					float z;
				};

				struct
				{
					float Px;
					float Py;
					float Pz;
				};

				struct
				{
					Point P;
				};
			};
		};

		struct RHW
		{
			union
			{
				float rhw;
				float w;
				float Pw;
			};
		};

		struct Normal
		{
			union
			{
				struct
				{
					float Nx;
					float Ny;
					float Nz;
				};

				struct
				{
					Vector N;
				};
			};
		};

		struct Diffuse 
		{
			union
			{
				struct
				{
					float Cb;
					float Cg;
					float Cr;
					float Ca;
				};

				struct
				{
					Color<float> C;
				};
			};
		};

		struct Specular
		{
			union
			{
				struct
				{
					float Lb;
					float Lg;
					float Lr;
					float La;
				};

				struct
				{
					Color<float> L;
				};
			};
		};

		struct Tex0
		{
			union
			{
				struct
				{
					float u;
					float v;
				};

				UV T[1];

				struct
				{
					float u0;
					float v0;
				};
			};
		};

		struct Tex1
		{
			union
			{
				float s;
				float u1;
			};

			union
			{
				float t;
				float v1;
			};
		};

		struct Tex2
		{
			float u2;
			float v2;
		};

		struct Tex3
		{
			float u3;
			float v3;
		};

		struct Tex4
		{
			float u4;
			float v4;
		};

		struct Tex5
		{
			float u5;
			float v5;
		};

		struct Tex6
		{
			float u6;
			float v6;
		};

		struct Tex7
		{
			float u7;
			float v7;
		};

	public:
		enum
		{
			hasRHW		= !!(flags & FVF_RHW),
			hasNormal	= !!(flags & FVF_NORMAL),
			hasDiffuse	= !!(flags & FVF_DIFFUSE),
			hasSpecular	= !!(flags & FVF_SPECULAR),
			hasTexture0	= !!(((flags & FVF_TEX_MASK) >> FVF_TEX_SHIFT) & (1 << 0)),
			hasTexture1	= !!(((flags & FVF_TEX_MASK) >> FVF_TEX_SHIFT) & (1 << 1)),
			hasTexture2	= !!(((flags & FVF_TEX_MASK) >> FVF_TEX_SHIFT) & (1 << 2)),
			hasTexture3	= !!(((flags & FVF_TEX_MASK) >> FVF_TEX_SHIFT) & (1 << 3)),
			hasTexture4	= !!(((flags & FVF_TEX_MASK) >> FVF_TEX_SHIFT) & (1 << 4)),
			hasTexture5	= !!(((flags & FVF_TEX_MASK) >> FVF_TEX_SHIFT) & (1 << 5)),
			hasTexture6	= !!(((flags & FVF_TEX_MASK) >> FVF_TEX_SHIFT) & (1 << 6)),
			hasTexture7	= !!(((flags & FVF_TEX_MASK) >> FVF_TEX_SHIFT) & (1 << 7))
		};

	private:
		typedef typename META_CONDITIONAL_INHERIT(hasRHW, Position, RHW) V1;
		typedef typename META_CONDITIONAL_INHERIT(hasNormal, V1, Normal) V2;
		typedef typename META_CONDITIONAL_INHERIT(hasDiffuse, V2, Diffuse) V3;
		typedef typename META_CONDITIONAL_INHERIT(hasSpecular, V3, Specular) V4;
		typedef typename META_CONDITIONAL_INHERIT(hasTexture0, V4, Tex0) V5;
		typedef typename META_CONDITIONAL_INHERIT(hasTexture1, V5, Tex1) V6;
		typedef typename META_CONDITIONAL_INHERIT(hasTexture2, V6, Tex2) V7;
		typedef typename META_CONDITIONAL_INHERIT(hasTexture3, V7, Tex3) V8;
		typedef typename META_CONDITIONAL_INHERIT(hasTexture4, V8, Tex4) V9;
		typedef typename META_CONDITIONAL_INHERIT(hasTexture5, V9, Tex5) V10;
		typedef typename META_CONDITIONAL_INHERIT(hasTexture6, V10, Tex6) V11;
		typedef typename META_CONDITIONAL_INHERIT(hasTexture7, V11, Tex7) V12;

	public:	
		enum
		{
			offRHW		= sizeof(Point),
			offNormal	= sizeof(V1),
			offDiffuse	= sizeof(V2),
			offSpecular	= sizeof(V3),
			offTexture0	= sizeof(V4),
			offTexture1	= sizeof(V5),
			offTexture2	= sizeof(V6),
			offTexture3	= sizeof(V7),
			offTexture4	= sizeof(V8),
			offTexture5	= sizeof(V9),
			offTexture6	= sizeof(V10),
			offTexture7	= sizeof(V11)
		};

		typedef typename ALIGN(16) V12 Res;
	};

	template<unsigned int flags>
	struct FVF : public FVFGenerator<flags>::Res, public FVFGenerator<flags>
	{
	};
}

#endif   // FVF_hpp