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

	Purpose: 
	
	OpenGL implementation of the Renderer.  Currently requires OpenGL 3.3.

	License:

	TODO
*/

#pragma once

#ifndef _ZOPENGLRENDERER_HPP
#define _ZOPENGLRENDERER_HPP

#include <SST/SST_GLAPI.h>
#include <SST/SST_WMOpenGL.h>

#include <ZRenderer/ZRendererBase.hpp>
#include <ZRenderer/ZOpenGLDataBuffer.hpp>
#include <ZRenderer/ZOpenGLShader.hpp>
#include <ZRenderer/ZOpenGLShaderProgram.hpp>
#include <ZRenderer/ZOpenGLDimensionTexture.hpp>
#include <ZRenderer/ZOpenGLSampler.hpp>
#include <ZRenderer/ZOpenGLFrameBufferRenderTarget.hpp>
#include <ZRenderer/ZOpenGLRenderBuffer.hpp>
#include <ZRenderer/ZOpenGLWindowRenderTarget.hpp>
#include <ZRenderer/ZOpenGLVertexParams.hpp>

//CHECKGL function, which checks for OpenGL Error conditions and asserts when they are found
void CheckGL();

//CHECKGL macro for debug mode
#if ZRENDERER_CHECKGL
	#define CHECKGL() (CheckGL())
#else
	#define CHECKGL()
#endif

//Enumeration for various OpenGL resource types
enum ZOpenGLResourceType
{
	ZOGLRT_BUFFER,			//OpenGL Buffer Object
	ZOGLRT_SHADER,			//OpenGL Shader Object
	ZOGLRT_SHADER_PROGRAM,	//OpenGL Shader Program Object
	ZOGLRT_TEXTURE,			//OpenGL Texture Object
	ZOGLRT_SAMPLER,			//OpenGL Sampler Object
	ZOGLRT_FRAME_BUFFER,	//OpenGL Frame Buffer Object
	ZOGLRT_RENDER_BUFFER,	//OpenGL Render Buffer Object
	ZOGLRT_VERTEX_ARRAY,	//OpenGL Vertex Array Object
	ZOGLRT_SIZE
};

/*
OpenGL Renderer implementation class.
*/
class ZOpenGLRenderer : public ZRendererBase
{
private:
	DISABLE_COPY_AND_ASSIGN(ZOpenGLRenderer);

	//Resource Generation Thread Request
	class ResourceGenerationThreadRequest : public ZThreadRequest
	{
	public:
		//Default Constructor
		ResourceGenerationThreadRequest() : ZThreadRequest(true) { }

		//Concurrency control lock for resource generation and cleanup
		ZMutex ResourceMutex;

		//Indicator that we have a pending resource request
		bool bPendingResourceRequest;

		//Resource Generation Requests
		ZArray< ZPtr<ZOpenGLDataBuffer> >							DataBufferGenerateRequests;
		ZArray< ZPtr<ZOpenGLShader> >								ShaderGenerateRequests;
		ZArray< ZPtr<ZOpenGLShaderProgram> >						ShaderProgramGenerateRequests;
		ZArray< ZPtr<ZOpenGLDimensionTexture> >					DimensionTextureGenerateRequests;
		ZArray<ZPtr<ZOpenGLSampler> >							SamplerGenerateRequests;
		ZArray< ZPtr<ZOpenGLFramebufferRenderTarget> >			FrameBufferGenerateRequests;
		ZArray< ZPtr<ZOpenGLRenderBuffer> >						RenderBufferGenerateRequests;
		ZArray< ZPtr<ZOpenGLVertexParams> >						VertexParamsGenerateRequests;

		//Resource Delete Requests
		ZArray< ZPair<ZOpenGLResourceType, GLuint> >	ResourceDeleteRequests;

		//Subclass Implementation
		virtual void Execute( ZThread *_threadObj );
	};

protected:
	//The initialization window
	SST_Window InitializationWindow;

	//The OpenGL Context for this renderer
	SST_OpenGLContext GLContext;

	//Indicates we own the GLContext
	bool bOwnsContext;

	//The Current Render State
	ZRenderState *CurrentRenderState;

	//Resource Request Object
	ZPtr<ResourceGenerationThreadRequest> ResourceThreadRequest;

	//Subclass Override
	virtual bool init();

	//Subclass Override
	virtual void initThread();
	
	//Subclass Override
	virtual void shutdown();

	//Subclass Override
	virtual void shutdownThread();

	//Subclass Override
	virtual void Draw(ZArray<ZDrawData*, 
							 ZArrayAllocator< ZDrawData*, ZRB_DEFAULT_DRAWDATA_BUFFER_SIZE> >& _renderList, 
					  ZRenderTarget* _renderTarget);

public:
	/*
	Constructor.

	Initializes this renderer with the given OpenGL context.

	The provided window handle is only used for binding to a window so that OpenGL values can be obtained.  In order
	to render to the provided window, a ZWindowRenderTarget will need to be created using that window handle after
	the renderer has finished initializing.

	@param _glContext - the OpenGL context to initialize this renderer with
	@param _window - window to initialize this renderer with
	@param _ownContext - indicates whether this renderer 'owns' the context and should clean up when destructed
	*/
	ZOpenGLRenderer(SST_OpenGLContext _glContext, SST_Window _window, bool _ownContext);

	/*
	Destructor.
	*/
	~ZOpenGLRenderer();

	/*
	public ZOpenGLRenderer::DeleteResource
	
	This will ask the renderer thread to clean up a previously generated OpenGL resource.
	
	@param _type - the type of resource 
	@param _handle - the handle to the resource
	@return (void)
	@context (all)
	*/
	void DeleteResource(ZOpenGLResourceType _type, GLuint _handle);

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

	//Subclass Override
	virtual ZPtr<ZDataBuffer> CreateDataBuffer(ZDataBufferType _type,
											   ZDataBufferUsage _usage, 
											   size_t _size);

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

	//Subclass Override
	virtual ZPtr<ZFramebufferRenderTarget> CreateFrameBufferRenderTarget(size_t _width, size_t _height);

	//Subclass Override
	virtual ZPtr<ZRenderBuffer> CreateRenderBuffer(ZRenderBufferType _type, size_t _width, size_t _height);

	//Subclass Override
	virtual ZPtr<ZSampler> CreateSampler();

	//Subclass Override
	virtual ZPtr<ZShader> CreateShader(ZShaderType _type);

	//Subclass Override
	virtual ZPtr<ZShaderProgram> CreateShaderProgram();

	//Subclass Override
	virtual ZPtr<ZTexture> CreateTexture(ZTextureType _type, ZTextureFormat _format, ZTextureUsage _usage, const ZBitmap& _bitmap, bool _generateMipmaps);

	//Subclass Override
	virtual ZPtr<ZRenderTarget> CreateWindowRenderTarget(SST_Window _window,
														 size_t _screenIndex);
};

#endif
