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

	Purpose: 
	
	Base implementation of the ZRenderer class, which handles many of the details that
	are common to multi threaded renderers.

	License:

	TODO
*/

#pragma once

#ifndef _ZRENDERERBASE_HPP
#define _ZRENDERERBASE_HPP

#include <ZRenderer/ZRenderer.hpp>
#include <ZRenderer/ZDataBufferBase.hpp>

//Forward Declarations
struct ZFrameData;

//Maximum number of concurrent frame contexts
#ifndef ZRB_MAX_FRAME_CONTEXT_COUNT
#define ZRB_MAX_FRAME_CONTEXT_COUNT			(4)
#endif

//Default slab allocation of Draw Objects, will auto correct at runtime if exceeded
#ifndef ZRB_DEFAULT_DRAWDATA_COUNT
#define ZRB_DEFAULT_DRAWDATA_COUNT			(128)	
#endif

//Default buffer size for a draw list
#ifndef ZRB_DEFAULT_DRAWDATA_BUFFER_SIZE
#define ZRB_DEFAULT_DRAWDATA_BUFFER_SIZE	(128)
#endif

/*
Used to contain only the minimum data necessary to draw geometry 
and hold no ownership (in smart pointer terms) over the parameters.
*/
struct ZDrawData
{
	ZShaderProgram*		ShaderProgram;		//The shader program we will be drawing with 
	ZShaderParams*		ShaderParams;		//The shader parameters we will be using
	ZVertexParams*		VertexParams;		//The vertex parameters we will be using
	ZIndexParams*		IndexParams;		//The index parameters we will be using
	ZRenderState		RenderState;		//The renderer state we will be using
	int					DrawGroup;			//The draw group for drawing data

	ZFrameData*			Parent;				//The parent ZFrameData struct that holds ownership

	//Default Constructor
	ZDrawData()
		: ShaderProgram(NULL), 
		  ShaderParams(NULL), VertexParams(NULL), IndexParams(NULL),
		  RenderState(), DrawGroup(-1),
		  Parent(NULL) 
	{ }
};

/*
Used to contain references to the data necessary to draw geometry per frame.  Holds
strong references long enough to ensure parameters do not get deleted.
*/
struct ZFrameData
{
	ZArray< ZPtr<ZShaderProgram> >	ShaderPrograms; //The shader programs we will be using
	ZArray< ZPtr<ZShaderParams>	>	ShaderParams;	//The shader parameters we will be using
	ZArray< ZPtr<ZVertexParams> >	VertexParams;	//The vertex parameters we will be using
	ZArray< ZPtr<ZIndexParams> >	IndexParams;	//The index parameters we will be using
	
	ZPtr<ZRenderTarget>				RenderTarget;	//The render target for this frame

	ZArray<ZDrawData*, 
		   ZArrayAllocator<ZDrawData*, ZRB_DEFAULT_DRAWDATA_BUFFER_SIZE> > DrawData;	//Our set of draw data

	//Concurrency Control Variables
	ZMutex FrameLock;
	ZEvent FrameEndSignal;
	ZEvent FrameStartSignal;
};

/*
ZRendererBase class, which can be extended by GL-specific renderers.
*/
class ZRendererBase : public ZRenderer
{
private:
	//The renderer lock
	ZMutex RendererLock;

	//Current Render Phase
	volatile ZRenderPhase Phase;

	//Slab allocator for ZFrameData
	ZSlabAllocator<ZDrawData, ZRB_DEFAULT_DRAWDATA_COUNT> DrawDataAllocator;

	//Slab allocator for ZFrameData
	ZSlabAllocator<ZFrameData, ZRB_MAX_FRAME_CONTEXT_COUNT> FrameDataAllocator;

	//The frame data available queue (available for use)
	ZRingBuffer<ZFrameData*, 
				ZRingBuffer_OverflowUnsafe, 
				ZArrayAllocator<ZFrameData*, ZRB_MAX_FRAME_CONTEXT_COUNT> > FrameAvailableQueue;

	//The frame data render queue (pending renders)
	ZRingBuffer<ZFrameData*, 
				ZRingBuffer_OverflowUnsafe,
				ZArrayAllocator<ZFrameData*, ZRB_MAX_FRAME_CONTEXT_COUNT> > FrameRenderQueue;

	//The frame data we are rendering
	ZFrameData* CurrentFrame;

protected:
	//Capabilities mapping
	ZHashMap<ZString, ZString> Capabilities;

	//Protected Constructor
	ZRendererBase();

	/*
	virtual protected ZRendererBase::init
	
	This will be called by ZRendererBase::Init and should handle subclass
	initialization.
	
	@return (bool) - true if successful, false otherwise
	*/
	virtual bool init() = 0;
	
	/*
	virtual protected ZRendererBase::shutdown
	
	This will be called by ZRendererBase::Shutdown and should handle subclass
	shutdown.
	
	@return (void)
	*/
	virtual void shutdown() = 0;

	/*
	virtual protected ZRendererBase::Draw
	
	Method which should, given a list of render data, draw it to the provided render target.
	
	@param _renderList - the sorted list of draw data objects to draw
	@param _renderTarget - the render target to draw to
	@return (void)
	*/
	virtual void Draw(ZArray<ZDrawData*, 
							 ZArrayAllocator<ZDrawData*, ZRB_DEFAULT_DRAWDATA_BUFFER_SIZE> >& _renderList,
					  ZRenderTarget* _renderTarget) = 0;


public:
	/*
	Destructor.
	*/
	virtual ~ZRendererBase();

	//////////////////////////
	/* Lifecycle Operations */
	//////////////////////////

	//Subclass Implementation
	virtual bool Init();

	//Subclass Implementation
	virtual void Shutdown();

	///////////////////////////
	/* Renderer Data Getters */
	///////////////////////////

	//Subclass Override
	virtual const ZHashMap<ZString, ZString>& GetCapabilities();

	//Subclass Override
	virtual ZRenderPhase GetRenderPhase();

	//////////////////////////
	/* Logistics Operations */
	//////////////////////////

	//Subclass Implementation
	virtual ZPtr<ZDrawParams> CreateDrawParams(ZDrawParamsType _type);

	//Not Implemented
	virtual ZPtr<ZDataBuffer> CreateDataBuffer(ZDataBufferType _type,
											   ZDataBufferUsage _usage, 
											   size_t _size
											   ) = 0;

	//Not Implemented
	virtual ZPtr<ZFramebufferRenderTarget> CreateFrameBufferRenderTarget(size_t _width, size_t _height) = 0;

	//Not Implemented
	virtual ZPtr<ZRenderBuffer> CreateRenderBuffer(ZRenderBufferType _type, size_t _width, size_t _height) = 0;

	//Not Implemented
	virtual ZPtr<ZSampler> CreateSampler() = 0;

	//Not Implemented
	virtual ZPtr<ZShader> CreateShader(ZShaderType _type) = 0;

	//Not Implemented
	virtual ZPtr<ZShaderProgram> CreateShaderProgram() = 0;

	//Not Implemented
	virtual ZPtr<ZTexture> CreateTexture(ZTextureType _type, ZTextureFormat _format, ZTextureUsage _usage, const ZBitmap& _bitmap, bool _generateMipmaps) = 0;

	//Not Implemented
	virtual ZPtr<ZRenderTarget> CreateWindowRenderTarget(SST_Window _window, size_t _screenIndex) = 0;

	////////////////////////
	/* Frame Data Getters */
	////////////////////////

	//Subclass Implementation
	virtual ZRenderTarget* GetFrameRenderTarget(ZFrameContext _frameContext);

	//////////////////////////
	/* Rendering Operations */
	//////////////////////////

	//Subclass Implementation
	virtual ZFrameContext BeginFrame(ZPtr<ZRenderTarget> _canvas);

	virtual void EndFrame(ZFrameContext _frameContext);

	//Subclass Implementation
	virtual void SubmitDrawData(ZFrameContext _frameContext,
								ZPtr<ZShaderProgram> _shaderProgram,
								ZPtr<ZShaderParams> _shaderParams,
								ZPtr<ZVertexParams> _vertexParams,
								ZPtr<ZIndexParams> _indexParams,
								const ZRenderState& _renderState,
								int _drawGroup = 0
								);

	//Subclass Implementation
	virtual void RenderFrame(ZFrameContext _frameContext);

	//Subclass Implementation
	virtual void WaitFrameStart(ZFrameContext _frameContext);

	//Subclass Implementation
	virtual void WaitFrameEnd(ZFrameContext _frameContext);

	//Implementation of the render thread
	virtual ZThreadReturn run(uint64_t _dt); 
};

#endif
