/*
	ZDataBuffer.hpp
	Author: James Russell <jcrussell@762studios.com>
	Created: 3/20/2011

	Purpose: 

	Interface for graphics device buffered memory, used for uniform buffers, vertex buffers, and 
	index buffers.

	Note that ZDataBuffer is a ZRendererResource, and should be used as such.

	License: 

	TODO
*/

#pragma once

#ifndef _ZDATABUFFER_HPP
#define _ZDATABUFFER_HPP

#include <ZRenderer/ZRendererResource.hpp>

#include <ZSTL/ZArray.hpp>

//Forward Declarations
class ZDataBuffer;

//The type of data buffer
enum ZDataBufferType
{
	ZDBT_VERTEX,			//Contains Vertex Data
	ZDBT_INDEX,				//Contains Index Data
	ZDBT_UNIFORM,			//Contains Uniform (Constant) Data
	ZDBT_SIZE
};

//The usage type of the data buffer
enum ZDataBufferUsage
{
	ZDBU_STATIC,			//Static Buffer (never or rarely updated)
	ZDBU_DYNAMIC,			//Dynamic Buffer (updated frequently)
	ZDBU_STREAMING,			//Streaming Buffer (updated every frame)
	ZDBU_SIZE
};

//The data type in a stream
enum ZDataBufferStreamType
{
	ZDBST_FLOAT32,	//32-bit floating point 
	ZDBST_FLOAT64,	//64-bit floating point
	ZDBST_INT8,		//8-bit integer, signed
	ZDBST_INT16,	//16-bit integer, signed
	ZDBST_INT32,	//32-bit integer, signed
	ZDBST_UINT8,	//8-bit integer, unsigned
	ZDBST_UINT16,	//16-bit integer, unsigned
	ZDBST_UINT32,	//32-bit integer, unsigned
	ZDBST_SIZE
};

//The data type in a block
enum ZDataBufferBlockType
{
	ZDBBT_UNIFORM,			//Uniform Data
	ZDBBT_INDEX8,			//8-bit index data
	ZDBBT_INDEX16,			//16-bit index data
	ZDBBT_INDEX32,			//32-bit index data
	ZDBBT_SIZE
};

//Struct for stream definitions
struct ZDataBufferStream
{
	ZDataBuffer* Buffer;			//Parent buffer that contains this stream
	size_t ElementSize;				//Size of an element in terms of data type (e.g., 2 for vec2, 3 for vec3, etc.)
	size_t Offset;					//Offset (in bytes) that this stream starts from beginning of data buffer
	size_t Stride;					//Distance (in bytes) between elements of this stream
	ZDataBufferStreamType Type;		//Type of data in each element of this stream (this gives us size of each unit of data)
	bool Normalize;					//true: fixed point value converted to [0,1] or [-1,1] range; false: typecasted.
};

//Struct for block definitions
struct ZDataBufferBlock
{
	ZDataBufferBlockType Type;		//Type of data contained by this block

	size_t Offset;					//Offset (in bytes) into the buffer where this block begins
	size_t Size;					//Size (in bytes) of the block

	ZDataBuffer* Buffer;			//Parent buffer that contains this block data
};

//Wrapper class for data buffers
class ZDataBuffer : public ZRendererResource
{
private:
	DISABLE_COPY_AND_ASSIGN(ZDataBuffer);

public:
	//Default Constructor
	ZDataBuffer() : ZRendererResource() { }

	//Virtual Destructor
	virtual ~ZDataBuffer() { }

	/*
	virtual public ZDataBuffer::DefineBlock
	
	Defines a block of data present in the buffer.  Examples of blocks include
	index blocks and uniform buffer storage blocks.
	
	@param _type - the type of block (index or uniform)
	@param _size - the size of the block (in bytes)
	@return (const ZDataBufferBlock*) - the data buffer block created
	@context (all)
	*/
	virtual const ZDataBufferBlock* DefineBlock(ZDataBufferBlockType _type,
												size_t _size
												) = 0;
	/*
	virtual public ZDataBuffer::DefineStream
	
	Defines a stream that is present in this buffer.  Examples of streams include 
	the position stream (vertex coordinates), the color stream, the normal stream, 
	and uv coordinate streams.  Other streams can be defined so as to be usable by 
	shader programs.

	@param _type - The type of stream.
	@param _elemCount - The number of elements in the stream, e.g., 3D position has an element 
					   count of 3, 2D position has an element count of 2, etc...
	@param _offset - The offset into the buffer (in bytes) where this stream begins
	@param _stride - The stride between beginnings of consecutive elements (in bytes), or 0 for packed elements
	@param _normalize - If true, fixed point data should be normalized (i.e. changed to [0,1] or [-1,1] if unsigned/signed), otherwise
						it is converted similar to a C typecast, i.e. 'float value = (float)fixedValue'
	@return (const ZDataBufferStream*) - the data buffer stream created
	@context (all)
	*/
	virtual const ZDataBufferStream* DefineStream(ZDataBufferStreamType _type,
												  size_t _elemCount,
												  size_t _offset,
												  size_t _stride,
												  bool _normalize
												  ) = 0;

	/*
	virtual public ZDataBuffer::Fill
	
	Fills the buffer with data, as specified.  This is equivalent to calling
	'MapBuffer', filling the buffer, and calling 'UnmapBuffer'.
	
	@param _data - the data to fill the buffer with
	@param _offset - offset into the buffer to start copying to
	@param _byteCount - number of bytes to copy from _data
	@return (bool) - true if this update was successful, false if the buffer is 'locked' or 'contended'
	@context (all)
	*/
	virtual bool Fill(const void* _data,
					  size_t _offset,
					  size_t _byteCount
					  ) = 0;

	/*
	virtual public ZDataBuffer::GetBlock
	
	Gets the pointer to a block previously defined using DefineBlock().

	@param index - The block's index. The first block created with DefineBlock() is 0, the second is 1, and so on.
	@return ( const ZDataBufferBlock*) - block definition
	@context (all)
	*/
	virtual const ZDataBufferBlock* GetBlock(size_t index) = 0;

	/*
	virtual public ZDataBuffer::GetStream
	
	Gets the pointer to a stream previously defined using DefineStream().
	
	@param index - The stream's index. The first stream created with DefineStream() is 0, the second is 1, and so on.
	@return (const ZDataBufferStream*) - stream definition
	@context (all)
	*/
	virtual const ZDataBufferStream* GetStream(size_t index) = 0;

	/*
	virtual public ZDataBuffer::GetBlockCount
	
	Gets the number of blocks that have been defined so far.
	
	@return (size_t) - The number of blocks defined with DefineBlock()
	@context (all)
	*/
	virtual size_t GetBlockCount() const = 0;

	/*
	virtual public ZDataBuffer::GetStreamCount
	
	Gets the number of streams that have been defined so far.
	
	@return (size_t) - The number of blocks defined with DefineStream()
	@context (all)
	*/
	virtual size_t GetStreamCount() const = 0;


	/*
	virtual public ZDataBuffer::GetSize
	
	Gets the size of this data buffer.
	
	@return (size_t) - the size (in bytes) of this buffer
	@context (all)
	*/
	virtual size_t GetSize() = 0;

	/*
	virtual public ZDataBuffer::GetType
	
	Gets the type of this data buffer.
	
	@return (ZDataBufferType) - the buffer type
	@context (all)
	*/
	virtual ZDataBufferType GetType() = 0;

	/*
	virtual public ZDataBuffer::GetUsage
	
	Gets the usage of this data buffer.
	
	@return (ZDataBufferUsage) - the usage type
	@context (all)
	*/
	virtual ZDataBufferUsage GetUsage() = 0;

	/*
	virtual public ZDataBuffer::Map
	
	Maps the data buffer into user memory and marks this resource as locked until 'UnmapBuffer' is called.

	If the discard parameter is set to true, this tells the implementation that the data
	that is currently present in the buffer is not required, and this may result in a faster
	return, especially in the case of static usage buffers.  If the current data is needed
	(discard is false), static usage buffers must wait for the render thread to get the data
	from the graphics memory.  Dynamic and streaming usage buffers typically suffer no 
	performance penalty regardless of the value of the discard parameter, though this may 
	vary based on implementation.

	@param _discard - hint to the implementation that the data in the buffer can be discarded
	@return (void*) - pointer to the buffer in mapped memory, NULL if this buffer is 'locked' or 'contended'
	@context (all)
	*/
	virtual void* Map(bool _discard = false) = 0;

	/*
	virtual public ZDataBuffer::UndefBlocks
	
	Undefines all currently set blocks.  This will invalidate existing block
	definition structures returned from DefineBlock.
	
	@return (void)
	@context (all)
	*/
	virtual void UndefBlocks() = 0;

	/*
	virtual public ZDataBuffer::UndefStreams
	
	Undefines all currently set streams.  This will invalidate existing stream
	definition structures returned from DefineStream.
	
	@return (void)
	@context (all)
	*/
	virtual void UndefStreams() = 0;

	/*
	virtual public ZDataBuffer::Unmap
	
	Unmaps a previously mapped data buffer and unlocks the data buffer.
	
	@return (void)
	@context (all)
	*/
	virtual void Unmap() = 0;
};

#endif