///////////////////////////////////////////////////////////////////////////////
// TnDX11RenderInterface.h

#pragma once
#ifndef TN_DX11_RENDER_INTERFACE_INCLUDED
#define TN_DX11_RENDER_INTERFACE_INCLUDED

#include "RenderInterface/TnRenderInterface.h"
#include "TnDX11RenderContext.h"
#include <stack>

// TODO: This is gross... what is the better way to hide these.
class DX11GpuBuffer;
typedef boost::shared_ptr<DX11GpuBuffer> DX11GpuBufferPtr;
class DX11InputLayout;
typedef boost::shared_ptr<DX11InputLayout> DX11InputLayoutPtr;
class DX11GpuProgram;
typedef boost::shared_ptr<DX11GpuProgram> DX11GpuProgramPtr;
class DX11Texture;
typedef boost::shared_ptr<DX11Texture> DX11TexturePtr;
class DX11FrameBufferOjbect;
typedef boost::shared_ptr<DX11FrameBufferOjbect> DX11FrameBufferOjbectPtr;

namespace Tn {
namespace Graphics {


	class DX11RenderInterface : public RenderInterface
	{		
	private:

		HRESULT m_lastError;

		DX11RenderContextPtr m_context;

		DX11InputLayoutPtr m_layout;
		bool m_layoutDirty;

		DX11GpuBufferPtr m_vertexBuffer;
		bool m_vertexBufferDirty;

		DX11GpuBufferPtr m_indexBuffer;
		bool m_indexBufferDirty;

		DX11GpuProgramPtr m_gpuProgram;
		bool m_gpuProgramDirty;

		static const int MaxTextures = 16;
		DX11TexturePtr m_textures[MaxTextures];
		uint16_t m_dirtyTextures;

		D3D11_DEPTH_STENCIL_DESC m_depthDesc;
        UINT m_stencilRef;
		bool m_depthDirty;

		D3D11_BLEND_DESC1 m_blendDesc;
		bool m_blendDirty;

		D3D11_RASTERIZER_DESC1 m_rasterDesc;
		D3D11_RECT m_scissorRect;
		bool m_rasterDirty;

		DX11FrameBufferOjbectPtr m_renderTarget;
		std::stack<DX11FrameBufferOjbectPtr> m_previousRenderTargets;

		std::vector<eGpuProgramType> m_supportedShaderTypes;

		void ApplyState();

		static D3D11_PRIMITIVE_TOPOLOGY ToPrimitiveTopology(ePrimitiveTopology topology);
		static D3D11_BLEND GetBlendFactor(eBlendFactor factor);
		static D3D11_BLEND GetBlendFactorAlpha(eBlendFactor factor);		
		static D3D11_COMPARISON_FUNC GetCompareFunction(eCompareFunction func);
        
        std::vector<eColorFormat> m_supportedCompressedTextureFormat;

	public:

		DX11RenderInterface();
		virtual ~DX11RenderInterface();

		virtual void Initialise(RenderContextPtr const & context);
        
        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, int32_t y, int32_t dx, int32_t dy);
		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;

		// FIXED FUNCTION - NOT IMPLEMENTED
		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();
	};

	typedef boost::shared_ptr<DX11RenderInterface> DX11RenderInterfacePtr;

} } // namespace Tn::Graphics

#endif // TN_DX11_RENDER_INTERFACE_INCLUDED
