///////////////////////////////////////////////////////////////////////////////
// TnRenderInterface.h

#pragma once
#ifndef TN_RENDER_INTERFACE_INCLUDED
#define TN_RENDER_INTERFACE_INCLUDED

#include "TnCore.h"

namespace Tn {
namespace Graphics {

///////////////////////////////////////////////////////////////////////////
// Input Layout

/// The type of elements in an input component.
/// @see InputComponent
enum eComponentType
{
    eComponentType_Byte,
    eComponentType_Unsigned_Byte,
    eComponentType_Int16,
    eComponentType_Int32,
    eComponentType_Uint16,
    eComponentType_Uint32,
    eComponentType_Float
};		

/// Defines one component of a vertex structure.
/// @see RenderInterface::CreateInputLayout
struct InputComponent
{
    /// The type of one element of this component. 
    eComponentType type;

    /// The number of elements in this component.
    uint32_t size;

    /// The name of this property in the 
    /// vertex shader.
    const char *name;

    /// The offset of the compoment in a stride
    size_t offset;

    /// The size of the compoment in a stride
    uint32_t step;

    /// Set to true if the values should be presented as 
    /// normalized to the shader in the -1,1 or 0,1 range.
    /// Only works for 8bit and 16bit integer formats.
    bool normalize;

    // Semantic index.
    uint32_t index;
};

/// Defines the GPU vertex input layout which is
/// used to map the vertex buffer data with the 
/// vertex shader.
/// @see RenderInterface::CreateInputLayout
class InputLayout
{
public:
    virtual ~InputLayout() {}
};

/// Shared pointer to an InputLayout. 
typedef Tn::Foundation::shared_ptr<InputLayout> InputLayoutPtr;


///////////////////////////////////////////////////////////////////////////
// GPU Program

/// The type of shader
/// @see RenderInterface::GetSupportedGpuProgramTypes
enum eGpuProgramType
{
    eGpuProgramType_GLSL_text,
    eGpuProgramType_GLSL_binary,
    eGpuProgramType_HLSL_text,
    eGpuProgramType_HLSL_binary
};

/// The block of shader constants that can be set on a GpuProgram.
/// @see GpuProgram::CreateParams
class GpuProgramParam
{
public:
    virtual ~GpuProgramParam() {}

    /// Set a single integer shader constant.
    virtual void SetInteger(std::string const & name, int32_t v) = 0;

    /// Set an array of integer shader constants.
    virtual void SetInteger(std::string const & name, uint32_t count, int32_t const v[]) = 0;

    /// Set a single float shader constant.
    virtual void SetFloat(std::string const & name, float v) = 0;

    /// Set an array of float shader constant.
    virtual void SetFloat(std::string const & name, uint32_t count, float const v[]) = 0;

    /// Set an array of matrix shader constants.
    virtual void SetMatrix(std::string const & name, uint32_t count, float const v[], bool transpose = false) = 0;

    /// Called to update the GPU program with the constant data.
    virtual void Apply() = 0;
};

/// Shared pointer to an GpuProgramParam. 
typedef Tn::Foundation::shared_ptr<GpuProgramParam> GpuProgramParamPtr;

/// Structure used to define a shader program 
/// created via the RenderInterface.
/// @see RenderInterface::CreateGpuProgram
struct GpuProgramCode
{
    BytesPtr vertexShader;
    uint32_t sizeVertexShader;
    BytesPtr pixelShader;
    uint32_t sizePixelShader;
    BytesPtr unifiedShader;
    uint32_t sizeUnifiedShader;
    eGpuProgramType shaderType;
};

/// Shared pointer to an GpuProgramCode
typedef Tn::Foundation::shared_ptr<GpuProgramCode> GpuProgramCodePtr;

/// A GPU program consisting of a vertex and pixel shader.
/// @see RenderInterface::CreateGpuProgram
class GpuProgram
{
public:
    virtual ~GpuProgram() {}

    /// Create a block of shader constants which is
    /// used to pass uniform state to the GPU.
    virtual GpuProgramParamPtr CreateParams() = 0;

    /// Create the input vertex layout for the vertex shader.
    virtual InputLayoutPtr CreateInputLayout(InputComponent component[], uint32_t count) = 0;

    /// Get the binary shaders
    virtual GpuProgramCodePtr GetProgramCodeCompiled() = 0;
};

/// Shared pointer to an GpuProgram. 
typedef Tn::Foundation::shared_ptr<GpuProgram> GpuProgramPtr;


///////////////////////////////////////////////////////////////////////////
// Frame Buffer

/// Manages a group of attached images for offscreen rendering.
/// @see RenderInterface::CreateFrameBufferObject, RenderInterface::AttachTextureFBO
class FrameBufferOjbect
{
public:
    virtual ~FrameBufferOjbect() {}
};

/// Shared pointer to an FrameBufferOjbect. 
typedef Tn::Foundation::shared_ptr<FrameBufferOjbect> FrameBufferOjbectPtr;


///////////////////////////////////////////////////////////////////////////
// GpuBuffer

/// Defines a hint to the GPU hardware as to
/// the expected usage of a GpuBuffer.
/// @see GpuBufferParam
enum eBufferUsage
{
    eBufferUsage_Static_Draw,
    eBufferUsage_Static_Read,
    eBufferUsage_Static_Copy,
    eBufferUsage_Dynamic_Draw,
    eBufferUsage_Dynamic_Read,
    eBufferUsage_Dynamic_Copy
};

/// To which input stream the GpuBuffer will be bound.
/// @see GpuBufferParam
enum eBindTarget
{
    eBindTarget_Vertex_Buffer,
    eBindTarget_Index_Buffer,
};

/// Structure used to define a GPUBuffer to
/// be created via the RenderInterface.
/// @see RenderInterface::CreateGpuBuffer
struct GpuBufferParam
{
    eBindTarget target;
    eBufferUsage usage;
    uint8_t *data;
    uint32_t size;
};

/// A hardware GPU buffer which contains data
/// to be read from the vertex shader.
/// @see RenderInterface::CreateGpuBuffer
class GpuBuffer
{
public:
    virtual ~GpuBuffer() {}
};

/// Shared pointer to an GpuBuffer.
typedef Tn::Foundation::shared_ptr<GpuBuffer> GpuBufferPtr;


/// Thread safe GPU buffer graveyard
class GpuBufferGraveyard
{
public:
    void push(GpuBufferPtr const & buffer)
    {
        Tn::Foundation::lock_guard<Tn::Foundation::mutex> guard(m_mutex);

        m_graveyard.insert(buffer);
    }

    void prune()
    {
        Tn::Foundation::lock_guard<Tn::Foundation::mutex> guard(m_mutex);

        for (std::set<GpuBufferPtr>::iterator it = m_graveyard.begin();
             it != m_graveyard.end();)
        {
            std::set<GpuBufferPtr>::iterator tmp = it;
            ++it;
            if (tmp->unique())
            {
                m_graveyard.erase(tmp);
            }
        }
    }

    ~GpuBufferGraveyard()
    {
        prune();
    }

private:
    Tn::Foundation::mutex m_mutex;
    std::set<GpuBufferPtr> m_graveyard;
};

/// Shared pointer to an GpuBufferGraveyard.
typedef Tn::Foundation::shared_ptr<GpuBufferGraveyard> GpuBufferGraveyardPtr;


///////////////////////////////////////////////////////////////////////////
// Texture

/// A color format used by a texture.
/// @see Texture2DParam
enum eColorFormat
{
    eColorFormat_Alpha,
    eColorFormat_Alpha16,
    eColorFormat_LuminanceAlpha,
    eColorFormat_RGB,
    eColorFormat_RGBA,
    
    /// Compressed format
    eColorFormat_RGB_S3TC_DXT1,
    eColorFormat_RGBA_S3TC_DXT1,
    eColorFormat_RGBA_S3TC_DXT3,
    eColorFormat_RGBA_S3TC_DXT5,
    eColorFormat_RGB_PVRTC_4BPPV1,
    eColorFormat_RGB_PVRTC_2BPPV1,
    eColorFormat_RGBA_PVRTC_4BPPV1,
    eColorFormat_RGBA_PVRTC_2BPPV1,
};

/// Structure used to define a Texture to
/// be created via the RenderInterface.
/// @see RenderInterface::CreateTexture2D, RenderInterface::UpdateTexture2D
struct Texture2DParam
{
    /// The width of the texture being defined.
    /// Must be greater than 0.
    uint32_t width;

    /// The height of the texture being defined.
    /// Must be greater than 0.
    uint32_t height;

    /// The color format of the texture data.
    eColorFormat format;

    /// If true then automatically generate a 
    /// complete mipmap chain.
    bool autoMipsOnCreate;

    /// If true then automatically generate a 
    /// complete mipmap chain.
    bool autoMipsOnUpdate;
};


/// A GPU texture which contains pixel data 
/// which can be sampled from a shader.
/// @see RenderInterface::CreateTexture2D, RenderInterface::UpdateTexture2D
class Texture
{
public:
    virtual ~Texture() {}
};

/// Shared pointer to a Texture.
typedef Tn::Foundation::shared_ptr<Texture> TexturePtr;

/// The texture sampler mode.
/// @see SamplerStateParam
enum eSamplerFilter
{
    /// Nearest point sampling.
    eSamplerFilter_Nearest,

    /// Linear filtering of neighboring samples.
    eSamplerFilter_Linear
};

/// The addressing mode used when sampling 
/// outside the edges of a texture.
/// @see SamplerStateParam
enum eSamplerAddressMode
{
    eSamplerAddressMode_Wrap,
    eSamplerAddressMode_Clamp
};

/// Structure used to define a SamplerState to be
/// created via the RenderInterface.
/// @see RenderInterface::CreateSamplerState
struct SamplerStateParam
{
    /// The mode used for minification, magnification, 
    /// and mipmap filtering.
    eSamplerFilter filter;

    /// The sampler address mode along the horizontal axis.
    eSamplerAddressMode addressS;

    /// The sampler address mode along the vertical axis.
    eSamplerAddressMode addressT;
};

/// Used to define how a texture is sampled from a shader.
/// @see RenderInterface::CreateSamplerState
class SamplerState
{
public:
    virtual ~SamplerState() {}
};

/// Shared pointer to a SamplerState.
typedef Tn::Foundation::shared_ptr<SamplerState> SamplerStatePtr;


///////////////////////////////////////////////////////////////////////////
// Fixed Function

/// The fixed function matrix type.
/// @see RenderInterface::PushMatrix
enum eMatrixMode
{
    eMatrixMode_Modelview,
    eMatrixMode_Projection,
    eMatrixMode_Texture
};

/// The fixed function fog mode.
/// @see RenderInterface::SetFogParam
enum eFogMode
{
    eFogMode_Linear,
    eFogMode_Exp,
    eFogMode_Exp2
};


///////////////////////////////////////////////////////////////////////////
// Render Context

/// The abstract interface to a platform specific rendering context
/// used to create the graphics rendering system.
/// @see GLESRenderContext, DX11RenderContext
class RenderContext
{
public:
    virtual ~RenderContext() {}
};

/// Shared pointer to a RenderContext.
typedef Tn::Foundation::shared_ptr<RenderContext> RenderContextPtr;


///////////////////////////////////////////////////////////////////////////
// Render Interface

/// An error message string.
/// @see RenderInterface::GetError
class Error : public std::string
{
public:

    Error() {}
    Error(std::string const & str)
        : std::string(str) {}

    /// The value returned when there is no error.
    static const Error NoError;
};

/// The primitive types used when drawing.
/// @see RenderInterface::DrawArray, RenderInterface::DrawIndexed
enum ePrimitiveTopology
{
    ePrimitiveTopology_Lines,
    ePrimitiveTopology_Line_Strip,
    ePrimitiveTopology_Triangles,
    ePrimitiveTopology_Triangles_Strip
};

/// The hardware culling state when drawing triangle
/// lists and triangle strips.
/// @see RenderInterface::SetCullFaceParam
enum eCullFaceOption
{
    /// Disable face culling.
    eCullFaceOption_None,

    /// Cull front faces with a 
    /// clockwise winding order.
    eCullFaceOption_Front,

    /// Cull back faces with a counter 
    /// clockwise winding order.
    eCullFaceOption_Back
};

/// Comparision function for depth and alpha test.
enum eCompareFunction
{
    /// Passes the test if the new value is less than the stored value.
    eCompareFunction_Less,

    /// Passes the test if the new value is less than or equal to the stored value.
    eCompareFunction_LessEqual,

    /// Passes the test if the new value is greater than the stored value.
    eCompareFunction_Greater,

    /// Passes the test if the new value is greater than or equal to the stored value.
    eCompareFunction_GreaterEqual,

    /// Passes the test if the new value is equal to the stored value.
    eCompareFunction_Equal,

    /// Passes the test if the new value is not equal to the stored value.
    eCompareFunction_NotEqual,

    /// Never passes the test regardless of either value.
    eCompareFunction_Never,

    /// Always passes the test regardless of either value.
    eCompareFunction_Always
};
    
enum eStencilOperation
{
    eStencilOperation_Keep,
    
    eStencilOperation_Zero,
    
    eStencilOperation_Replace,
    
    eStencilOperation_Increment,
    
    eStencilOperation_IncremntWrap,
    
    eStencilOperation_Decrement,
    
    eStencilOperation_DecrementWrap,
    
    eStencilOperation_Invert
};

enum eBlendFactor
{
    eBlendFactor_Zero,
    eBlendFactor_One,
    eBlendFactor_Src_Color,
    eBlendFactor_One_Minus_Src_Color,
    eBlendFactor_Dst_Color,
    eBlendFactor_One_Minus_Dst_Color,
    eBlendFactor_Src_Alpha,
    eBlendFactor_One_Minus_Src_Alpha,
    eBlendFactor_Dst_Alpha,
    eBlendFactor_One_Minus_Dst_Alpha
};


/// The abstract interface for rendering via the harware GPU.
/// @see GLESRenderInterface, DX11RenderInterface
class RenderInterface
{		
public:
    virtual ~RenderInterface() {}

    /// Assign the RenderContext to the render interface and prepare
    /// it for use.  This must be called before any drawing begins.
    /// Currently only DX implementation uses this (hence, not must implement)
    /// @see RenderContext
    virtual void Initialise(RenderContextPtr const & context);
    
    /*! 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 *));
    
    /// Query for number of vertex texture units supported
    virtual int VertexTextureUnitsSupported() const = 0;

    /// Called each frame before any drawing is done.
    virtual void Begin() = 0;

    /// Create a sampler state which is used for texture creation.
    /// @see CreateTexture2D
    virtual SamplerStatePtr CreateSamplerState(SamplerStateParam const & param) = 0;

    /// Get the supported compressed texture color format
    virtual std::vector<eColorFormat> const & GetSupportedCompressedTextureFormat() = 0;

    /// Create a 2D texture.
    TNMAP_DEPRECATED(
    virtual TexturePtr CreateTexture2D(Texture2DParam const & param,
                                       std::vector<uint8_t*> const & byteVector,
                                       SamplerStatePtr state) = 0);
    
    virtual TexturePtr CreateTexture2D(Texture2DParam const & param,
                                       std::vector<std::vector<uint8_t> > const & byteVector,
                                       SamplerStatePtr state) = 0;

    /// Update the content of an existing texture.
    TNMAP_DEPRECATED(
    virtual void UpdateTexture2D(TexturePtr const & texture, Texture2DParam const & param, std::vector<uint8_t*> const & data, uint32_t xOffset, uint32_t yOffset) = 0);

    virtual void UpdateTexture2D(TexturePtr const & texture, Texture2DParam const & param, std::vector<std::vector<uint8_t> > const & data, uint32_t xOffset, uint32_t yOffset) = 0;
    
    /// Set the texture on the pixel shader texture slot.
    virtual void BindTexture(TexturePtr const & texture, uint32_t slot) = 0;

    /// Set textures in order on the pixel shader texture slots.
    virtual void BindTextures(TexturePtr texture[], uint32_t count) = 0;

    /// Remove the texture from the pixel shader texture slot.
    virtual void UnbindTexture(uint32_t slot) = 0;

    /// Remove textures from multiple pixel shader texture slots.
    virtual void UnbindTextures(uint32_t count) = 0;

    /// Create hardware GPU buffer which contains data
    /// to be read from the vertex shader.
    virtual GpuBufferPtr CreateGpuBuffer(GpuBufferParam const & param) = 0;

    /// Update a GPU buffer.
    virtual void UpdateGpuBuffer(GpuBufferPtr const & buffer, uint8_t * data, uint32_t size, uint32_t offset) = 0;

    /// Set the buffer on the device for rendering.
    virtual void BindGpuBuffer(GpuBufferPtr const & buffer) = 0;

    /// Remove the buffer from the device.
    virtual void UnbindGpuBuffer(GpuBufferPtr const & buffer) = 0;

    /// Set the input layout for rendering.
    virtual void BindInputLayout(InputLayoutPtr const & layout) = 0;

    /// Remove the input layout from the device.
    virtual void UnbindInputLayout(InputLayoutPtr const & layout) = 0;

    /// Draw using the bound vertex buffer.
    virtual void DrawArray(ePrimitiveTopology topology, uint32_t base, uint32_t count) = 0;

    /// Draw using the bound vertex and index buffers.
    virtual void DrawIndexed(ePrimitiveTopology topology, uint32_t base, uint32_t count) = 0;

    /// Get the supported shader types in order of favorite
    virtual std::vector<eGpuProgramType> const & GetSupportedGpuProgramTypes() = 0;

    /// Create a program for running shaders on the GPU.
    virtual GpuProgramPtr CreateGpuProgram(GpuProgramCode const & code, std::string & errorMsg) = 0;

    /// Set the shader program on the device for rendering.
    virtual void BindGpuProgram(GpuProgramPtr const & program) = 0;

    /// Remove the shader program from the device.
    virtual void UnbindGpuProgram(GpuProgramPtr const & program) = 0;

    /// Create a frame buffer object for offscreen rendering.  Note
    /// that you must attach a color texture before it can be used.
    /// @see AttachTextureFBO, BindFrameBufferObject
    virtual FrameBufferOjbectPtr CreateFrameBufferObject(uint32_t width, uint32_t height) = 0;

    /// Set a texture to act as the color target for the frame buffer object.
    virtual void AttachTextureFBO(FrameBufferOjbectPtr const & fbo, TexturePtr texture) = 0;

    /// Set the frame buffer object as the new render target.  You can
    /// pass a null FrameBufferOjbectPtr to restore the default render target.
    /// @see CreateFrameBufferObject
    virtual void BindFrameBufferObject(FrameBufferOjbectPtr const & fbo) = 0;

    /// Removes the frame buffer object if it is the current one set
    /// and restores the default render target.
    /// @see CreateFrameBufferObject
    virtual void UnbindFrameBufferObject(FrameBufferOjbectPtr const & fbo) = 0;

    /// Fill the texture matrix for using the FBO as a texture
    virtual void FillFrameBufferObjectTextureMatrix(float matrix[4][4]) = 0;

    /// Clear the active frame buffer.  Optionally clear depth to 1.
    virtual void ClearFramebuffer(float r, float g, float b, float a, bool clearDepth) = 0;

    /// Set the subset of the active frame buffer for rendering.
    virtual void SetViewPort(int32_t x, int32_t y, int32_t dx, int32_t dy) = 0;
    
    virtual void EnableStencilTest(bool enable) = 0;
    virtual void ClearStencilBuffer(int32_t clearValue) = 0;
    virtual void SetStencilFunction(eCompareFunction func, uint8_t ref, uint8_t mask) = 0;
    virtual void SetStencilOperation(eStencilOperation stencilFail, //stencil failed
                                     eStencilOperation depthTestFail, //stencil failed, depth test passed
                                     eStencilOperation bothPassed) = 0; //both passed

    /// Enable or disable the rectanglar scissor clipping region.  Note
    /// that the scissor state does not affect the results of ClearFramebuffer.
    virtual void SetScissorTest(bool enableTest, int32_t x = 0, int32_t y = 0, int32_t dx = 0, int32_t dy = 0) = 0;

    /// Set the state for reading and writing to the depth buffer.
    virtual void SetDepthBufferParam(bool enableCheck, bool enableWrite = true, eCompareFunction func = eCompareFunction_Less) = 0;

    /// @see SetDepthBufferParam
    virtual void EnableDepthBufferTest(bool enable) = 0;

    /// @see SetDepthBufferParam
    virtual void EnableDepthBufferWrite(bool enable) = 0;

    /// @see SetDepthBufferParam
    virtual void SetDepthBufferFunction(eCompareFunction func) = 0;

    /// Enables alpha blending with the frame buffer defining
    /// the blend math for the source and destination pixels.
    virtual void SetBlendParam(bool enable, eBlendFactor src = eBlendFactor_One, eBlendFactor dst = eBlendFactor_Zero) = 0;

    ///
    virtual void SetPolygonOffsetParam(bool enable, float factor = 0.0f, float unit = 0.0f) = 0;

    /// Set the hardware culling mode when drawing 
    /// triangle lists and triangle strips.
    virtual void SetCullFaceParam(eCullFaceOption) = 0;

    // NOT IMPLEMENTED!
    virtual void ReadPixels(uint32_t xOffset, uint32_t yOffset, uint32_t width, uint32_t height, BytesPtr pixels) = 0;


    /// Returns any error messages from the previous API
    /// call or Error::None if no errors have occured.
    virtual Error GetError() const = 0;


    ///////////////////////////////////////////////////////////////////////
    /// Fixed Function

    virtual InputLayoutPtr CreateInputLayout(InputComponent component[], uint32_t count) = 0;

    virtual void SetColor(float r, float b, float g, float a) = 0;

    virtual void SetAlphaTestParam(bool enable, float value, eCompareFunction func) = 0;

    virtual void EnableAlphaTest(bool enable) = 0;

    virtual void SetFogParam(bool enable, eFogMode mode, float density, float begin, float end, float index, float color[]) = 0;

    virtual void EnableFog(bool enable) = 0;

    /// Push a column-major matrix on to the stack.
    virtual void PushMatrix(eMatrixMode mode, float matrix[]) = 0;

    /// Pop the last matrix off the stack.
    virtual void PopMatrix(eMatrixMode mode) = 0;

    /// GPU buffer graveyard for safe destruction
    virtual GpuBufferGraveyardPtr GetGpuBufferGraveyard() = 0; 

};

/// Shared pointer to a RenderInterface.
typedef Tn::Foundation::shared_ptr<RenderInterface> RenderInterfacePtr;
typedef Tn::Foundation::weak_ptr<RenderInterface> RenderInterfaceWeakPtr;
} } // namespace Tn::Graphics

#endif // TN_RENDER_INTERFACE_INCLUDED
