///////////////////////////////////////////////////////////////////////////////
// TnGLESRenderInterface.h

#pragma once
#ifndef TN_GLES_RENDER_INTERFACE_INCLUDED
#define TN_GLES_RENDER_INTERFACE_INCLUDED

#include <RenderInterface/TnRenderInterface.h>
#include <RenderInterface/GLES/TnGl.h>
#include <map>
#include <stack>

class GLESGpuProgramParam;

namespace Tn {
namespace Graphics {

	class GLESRenderInterface : public RenderInterface
	{		
	protected:

        //globally namespaced GLESGpuProgramParam calls ApplyState()
		friend class ::GLESGpuProgramParam;

		static GLenum GetCompareFunction(eCompareFunction func);
        static GLenum GetStencilOperation(eStencilOperation op);
        static GLenum GetPrimitiveMode(ePrimitiveTopology prim);
        static GLenum GetBlendFactor(eBlendFactor factor);

		void ApplyState();
        void ApplyScissor();

		bool m_scissorEnable;
		int32_t m_scissorX, m_scissorY, m_scissorDX, m_scissorDY;
		bool m_scissorDirty;

		bool m_depthEnable, m_depthWrite;
		eCompareFunction m_depthFunc;
		bool m_depthDirty;

		bool m_blendEnable;
		eBlendFactor m_blendSrc, m_blendDst;
		bool m_blendDirty;
        
        bool m_stencilEnable, m_clearStencil;
        int32_t m_clearStencilValue;
        eStencilOperation m_stencilOpFail, m_stencilOpDepthFail, m_stencilOpPassed;
        eCompareFunction  m_stencilFunction;
        uint8_t m_stencilRef, m_stencilMask;
        bool m_stencilDirty;

		bool m_polyOffsetEnable;
		float m_polyOffsetFactor, m_polyOffsetUnit;
		bool m_polyOffsetDirty;

		eCullFaceOption m_cull;
		bool m_cullDirty;

		GLint m_maxVertexAttributes;

		GpuBufferPtr m_vertexBuffer;
		InputLayoutPtr m_vertexLayout;
		uint32_t m_vertexAttributes;
		bool m_vertexBufferDirty;

		GpuBufferPtr m_indexBuffer;
		bool m_indexBufferDirty;

		static const int MaxTextures = 16;
		TexturePtr m_textures[MaxTextures];
		uint32_t m_dirtyTextures;

		GpuProgramPtr m_gpuProgram;
		bool m_gpuProgramDirty;
        
        std::vector<eGpuProgramType> m_supportedShaderTypes;

		void ApplyProgram(GLuint program);
        
        std::vector<eColorFormat> m_supportedCompressedTextureFormat;
        typedef std::map<eColorFormat, std::pair<GLenum, bool> > TextureFormatMap;
        TextureFormatMap m_textureFormatMap;
        
        std::stack<uint32_t> m_previousFrameBufferIDs;

        GpuBufferGraveyardPtr m_gpuBufferGraveyard;

	public:

		GLESRenderInterface();
		virtual ~GLESRenderInterface();
        
        /*! Needs to be called once by some platforms (MS), and only once before any other engine entry
         *  points are called. Currently this only is used in the GL implementation (hence, not must implement)
         */
        virtual bool InitGraphicsLibrary(void* (*pGetProAddrFunc)(const char *));
        
        
        virtual int VertexTextureUnitsSupported() const;

		virtual void Begin();

		virtual SamplerStatePtr CreateSamplerState(SamplerStateParam const & param);

        virtual std::vector<eColorFormat> const & GetSupportedCompressedTextureFormat() {return m_supportedCompressedTextureFormat; };
		virtual TexturePtr CreateTexture2D(Texture2DParam const & param, std::vector<uint8_t*> const & data, SamplerStatePtr state);
        virtual TexturePtr CreateTexture2D(Texture2DParam const & param, std::vector<std::vector<uint8_t> > const & data, SamplerStatePtr state);
		virtual void UpdateTexture2D(TexturePtr const & texture, Texture2DParam const & param, std::vector<uint8_t*> const & data, uint32_t xOffset, uint32_t yOffset);
        virtual void UpdateTexture2D(TexturePtr const & texture, Texture2DParam const & param, std::vector<std::vector<uint8_t> > const & data, uint32_t xOffset, uint32_t yOffset);
		virtual void BindTexture(TexturePtr const & texture, uint32_t slot);
		virtual void BindTextures(TexturePtr texture[], uint32_t count);
		virtual void UnbindTexture(uint32_t slot);
		virtual void UnbindTextures(uint32_t count);

		virtual GpuBufferPtr CreateGpuBuffer(GpuBufferParam const & param);
		virtual void UpdateGpuBuffer(GpuBufferPtr const & buffer, uint8_t * data, uint32_t size, uint32_t offset);
		virtual void BindGpuBuffer(GpuBufferPtr const & buffer);
		virtual void UnbindGpuBuffer(GpuBufferPtr const & buffer);

		virtual void BindInputLayout(InputLayoutPtr const & layout);
		virtual void UnbindInputLayout(InputLayoutPtr const & layout);

        virtual void DrawArray(ePrimitiveTopology topology, uint32_t base, uint32_t count);
        virtual void DrawIndexed(ePrimitiveTopology topology, uint32_t base, uint32_t count);

        virtual std::vector<eGpuProgramType> const & GetSupportedGpuProgramTypes();

		virtual GpuProgramPtr CreateGpuProgram(GpuProgramCode const & code, std::string & errorMsg);
		virtual void BindGpuProgram(GpuProgramPtr const & program);
		virtual void UnbindGpuProgram(GpuProgramPtr const & program);

		virtual FrameBufferOjbectPtr CreateFrameBufferObject(uint32_t width, uint32_t height);
		virtual void AttachTextureFBO(FrameBufferOjbectPtr const & fbo, TexturePtr texture);
		virtual void BindFrameBufferObject(FrameBufferOjbectPtr const & fbo);
		virtual void UnbindFrameBufferObject(FrameBufferOjbectPtr const & fbo);
        virtual void FillFrameBufferObjectTextureMatrix(float matrix[4][4]);

		virtual void ClearFramebuffer(float r, float g, float b, float a, bool clearDepth);
		virtual void SetViewPort(int32_t x, int32_t y, int32_t dx, int32_t dy);
        virtual void EnableStencilTest(bool enable);
        virtual void ClearStencilBuffer(int32_t clearValue);
        virtual void SetStencilFunction(eCompareFunction func, uint8_t ref, uint8_t mask);
        virtual void SetStencilOperation(eStencilOperation stencilFail,   //stencil failed
                                         eStencilOperation depthTestFail, //stencil failed, depth test passed
                                         eStencilOperation bothPassed);   //both passed
		virtual void SetScissorTest(bool enableTest, int32_t x = 0, int32_t y = 0, int32_t dx = 0, int32_t dy = 0);
		virtual void SetDepthBufferParam(bool enableCheck, bool enableWrite, eCompareFunction func);
		virtual void EnableDepthBufferTest(bool enable);
		virtual void EnableDepthBufferWrite(bool enable);
		virtual void SetDepthBufferFunction(eCompareFunction func);
		virtual void SetBlendParam(bool enable, eBlendFactor src, eBlendFactor dst);
		virtual void SetPolygonOffsetParam(bool enable, float factor, float unit);
		virtual void SetCullFaceParam(eCullFaceOption);

        virtual void ReadPixels(uint32_t xOffset, uint32_t yOffset, uint32_t width, uint32_t height, BytesPtr pixels);

        virtual Error GetError() const;

		virtual InputLayoutPtr CreateInputLayout(InputComponent component[], uint32_t count);
		virtual void SetColor(float r, float b, float g, float a);
		virtual void SetAlphaTestParam(bool enable, float value, eCompareFunction func);
		virtual void EnableAlphaTest(bool enable);
		virtual void SetFogParam(bool enable, eFogMode mode, float density, float begin, float end, float index, float color[]);
		virtual void EnableFog(bool enable);
		virtual void PushMatrix(eMatrixMode mode, float matrix[]);
		virtual void PopMatrix(eMatrixMode mode);

        virtual GpuBufferGraveyardPtr GetGpuBufferGraveyard();
	};

} } // namespace Tn::Graphics

#endif // TN_GLES_RENDER_INTERFACE_INCLUDED
