/*
	ZShaderProgram.hpp
	Author: Chris Ertel <crertel@762studios.com>,
			James Russell <jcrussell@762studios.com>
	Created: 04/03/2011

	Purpose: 

	TODO

	License:

	TODO
*/

#pragma once

#ifndef _ZSHADERPROGRAM_HPP
#define _ZSHADERPROGRAM_HPP

#include <ZRenderer/ZRendererBuild.hpp>
#include <ZRenderer/ZRendererResource.hpp>
#include <ZRenderer/ZShader.hpp>
#include <ZRenderer/ZSampler.hpp>

#include <ZSTL/ZString.hpp>

#include <ZUtil/ZUtil.hpp>

//Enumeration for shader stream attribute types
enum ZShaderStreamAttributeType
{
	// VECTOR TYPE
	ZSSAT_VECTOR_MIN,

	// Single-precision float
	ZSSAT_FLOAT,
	ZSSAT_FLOAT_VEC2,
	ZSSAT_FLOAT_VEC3,
	ZSSAT_FLOAT_VEC4,

	ZSSAT_VECTOR_MAX,

	// MATRIX TYPE
	ZSSAT_MATRIX_MIN,

	// Single-precision float matrix
	ZSSAT_MAT_22,
	ZSSAT_MAT_23,
	ZSSAT_MAT_24,
	ZSSAT_MAT_32,
	ZSSAT_MAT_33,
	ZSSAT_MAT_34,
	ZSSAT_MAT_42,
	ZSSAT_MAT_43,
	ZSSAT_MAT_44,

	ZSSAT_MATRIX_MAX,

	ZSSAT_SIZE,
	ZSSAT_UNKNOWN
};

//Enumeration for types of shader uniform block member types
enum ZShaderUniformBlockMemberType
{
	//VECTOR TYPE
	ZSUBMT_VECTOR_MIN,

	//Signed integer
	ZSUBMT_INT,
	ZSUBMT_INT_VEC2,
	ZSUBMT_INT_VEC3,
	ZSUBMT_INT_VEC4,

	//Unsigned integer
	ZSUBMT_UINT,
	ZSUBMT_UINT_VEC2,
	ZSUBMT_UINT_VEC3,
	ZSUBMT_UINT_VEC4,

	//Single-precision float
	ZSUBMT_FLOAT,
	ZSUBMT_FLOAT_VEC2,
	ZSUBMT_FLOAT_VEC3,
	ZSUBMT_FLOAT_VEC4,

	//Half-precision float
	ZSUBMT_HALF,
	ZSUBMT_HALF_VEC2,
	ZSUBMT_HALF_VEC3,
	ZSUBMT_HALF_VEC4,

	//Boolean
	ZSUBMT_BOOL,
	ZSUBMT_BOOL_VEC2,
	ZSUBMT_BOOL_VEC3,
	ZSUBMT_BOOL_VEC4,

	ZSUBMT_VECTOR_MAX,

	//MATRIX
	ZSUBMT_MATRIX_MIN,

	//Single-precision float matrix
	ZSUBMT_MAT_22,
	ZSUBMT_MAT_23,
	ZSUBMT_MAT_24,
	ZSUBMT_MAT_32,
	ZSUBMT_MAT_33,
	ZSUBMT_MAT_34,
	ZSUBMT_MAT_42,
	ZSUBMT_MAT_43,
	ZSUBMT_MAT_44,

	ZSUBMT_MATRIX_MAX,

	ZSUBMT_SIZE
};

//Enumeration of sampler types
enum ZShaderSamplerType
{
	ZSST_SAMPLER_1D,			//1D texture map
	ZSST_SAMPLER_2D,			//2D texture map
	ZSST_SAMPLER_3D,			//3D texture map
	ZSST_SAMPLER_CUBE,			//Cubic texture map (unsupported)
	ZSST_SAMPLER_1D_SHADOW,		//1D shadow map (unsupported)
	ZSST_SAMPLER_2D_SHADOW,		//2D shadow map (unsupported)
	ZSST_SIZE
};

//Struct defining a shader stream attribute
struct ZShaderStreamAttribute
{
	ZName  Name;						//The name of the attribute
	size_t Size;						//The size of the attribute in terms of elements (arrays have Size > 1)
	size_t Index;						//Index of this attribute in the attributes array

	ZShaderStreamAttributeType Type;	//The type of the attribute
};

//Struct defining a uniform block member
struct ZShaderUniformBlockMember
{
	ZName  Name;									//Name of the member
	size_t Offset;									//Offset (within the block) to the member
	size_t Size;									//Size of the member in terms of elements (arrays have Size > 1)
	size_t Index;									//Index of this member in the member array

	size_t ArrayStride;								//Stride between elements in an array (0 if not array)

	size_t MatrixStride;							//Stride between columns (or rows) of a matrix type (0 if not matrix type)
	enum { ROW_MAJOR, COLUMN_MAJOR } MatrixOrder;	//Order of a matrix type (row or column)

	ZShaderUniformBlockMemberType Type;				//Type of the member
};

//Struct defining a uniform block layout
struct ZShaderUniformBlock
{
	ZName  Name;								//Name of the block
	size_t Size;								//Size of the block (in bytes)
	size_t Index;								//Index of this block in the blocks array

	ZArray<ZShaderUniformBlockMember> Members;  //Members contained in the block

	/*
	public ZShaderUniformBlock::GetMemberByName

	This lookup method is used to get a pointer to a member by name, or NULL if not found.  
	Bear in mind that the name is qualified by the block name.

	@param _name - the qualified name of the parameter
	@return (const ZShaderUniformBlockMember*) - pointer to member if found, NULL if not found
	*/
	inline const ZShaderUniformBlockMember* GetMemberByName(const ZName& _name) const
	{
		for (size_t i = 0; i < Members.Size(); i++) {
			if (Members.Data()[i].Name == _name) {
				return &Members.Data()[i];
			}
		}

		return NULL;
	}
};

//Struct defining a shader sampler
struct ZShaderSampler
{
	ZName  Name;								//Name of the sampler
	size_t Index;								//Index of this sampler in the samplers array

	ZShaderSamplerType Type;					//Type of the sampler
};

/*
Interface for a shader program.
*/
class ZShaderProgram : public ZRendererResource
{
public:
	//Virtual Destructor
	virtual ~ZShaderProgram() { }

	/*
	virtual public ZShaderProgram::AttachShader

	Function to attach a shader.  This will compile the shader.

	@param _shader - The shader to attach to this shader program.
	@return (bool) - true if able to compile and attach the shader
	@context (all)
	*/
	virtual bool AttachShader(ZPtr<ZShader> _shader) = 0;

	/*
	virtual public ZShaderProgram::ClearLog

	Function to clear the log of a shader program.

	@return (void)
	@context (all)
	*/
	virtual void ClearLog() = 0;
	
	/*
	virtual public ZShaderProgram::DetachShaders

	Function to get the remove all attached shaders.

	@return (void)
	@context (all)
	*/
	virtual void DetachShaders() = 0;

	/*
	virtual public ZShaderProgram::GetLinkLog

	Function to get the link log of a shader program.

	@return (ZString) The log of the shader.
	@context (all)
	*/
	virtual const ZString& GetLinkLog() = 0;

	/*
	virtual public ZShaderProgram::GetShaders

	Function to get the an array of the currently attached shaders.

	@return (ZArray< ZPtr<ZShader> >)
	@context (all)
	*/
	virtual const ZArray< ZPtr<ZShader> >& GetShaders() = 0;

	/*
	virtual public ZShaderProgram::GetStreamAttributes

	Function to get the stream shader attributes declarations for a linked shader program.

	This returns an empty array until after linking.

	@return (const ZArray<ZShaderStreamAttribute>&) List of shader attributes.
	@context (all)
	*/
	virtual const ZArray<ZShaderStreamAttribute>& GetStreamDeclarations() = 0;
		
	/*
	virtual public ZShaderProgram::GetUniformBlockDeclarations
	
	Function to get the shader uniform block declarations for a linked shader program.

	This returns an empty array until after linking.
	
	@return (const ZArray<ZShaderUniformBlock>&)
	@context (all)
	*/
	virtual const ZArray<ZShaderUniformBlock>& GetUniformBlockDeclarations() = 0;

	/*
	virtual public ZShaderProgram::GetShaderSamplerDeclarations
	
	Function to get teh shader sampler declarations for a linked shader program.

	This returns an empty array until after linking.
	
	@return (const ZArray<ZShaderSampler>&)
	@context (all)
	*/
	virtual const ZArray<ZShaderSampler>& GetShaderSamplerDeclarations() = 0; 

	/*
	virtual public ZShaderProgram::Link

	Function to link the attached shaders into a usable shader program object.

	@return (bool) True if successfully linked, false otherwise.
	@context (all)
	*/
	virtual bool Link() = 0;

	/*
	virtual public ZShaderProgram::IsUsable

	Function to see if shader program is usable.

	@return (bool) True if shader program is usable, false otherwise.
	@context (all)
	*/
	virtual bool IsUsable() = 0;
};

#endif
