/*
 * This source file is part of KEOS (Free 3D Engine)
 * For the latest info, see http://www.keosengine.org/
 * E-mails : thierry.vouriot@keosengine.org, yeri@keosengine.org
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307, USA, or go to
 * http://www.gnu.org/copyleft/lesser.txt.
 *
 */

#ifndef KEOS_RENDERSYSTEM_H
#define KEOS_RENDERSYSTEM_H

#include "KeosPrerequisites.h"
#include "KeosRenderTarget.h"
#include "KeosRenderWindow.h"
#include "KeosRenderApp.h"
#include "KeosDeclaration.h"
#include "KeosBuffer.h"
#include "KeosRenderSystemCapabilities.h"
#include "KeosConfigOption.h"
#include "KeosColor.h"
#include "Cg/cg.h"

//@todo hummm.....
#define Renderer CRoot::Instance().GetRenderSystem()

namespace Keos
{
    typedef std::map< String, IRenderTarget * > RenderTargetMap;

    //-----------------------------------------------------------------------
    /** Abstract class for renderer systems.
     *
     */
    class KEOS_EXPORT IRenderSystem
    {
    public :

        /** Constructor.
         */
        IRenderSystem();

        /** Destructor.
         */
        virtual ~IRenderSystem();

		/** Returns the name of the rendering system.
         */
        virtual const String& GetName(void) const = 0;
       
        /** Start up the renderer using the settings selected (Or the defaults if none have been selected).
         *  @param bAutoCreateWindow If true, creates a render window
         *         automatically, based on settings chosen so far. This saves
         *         an extra call to RenderSystem::CreateRenderWindow
         *         for the main render window.
         *  @param strWindowTitle Window title.
         *  @returns A pointer to the automatically created window, if requested, otherwise null.
         */
        virtual IRenderWindow* Initialise(bool bAutoCreateWindow, const String& strWindowTitle = "KEOS Render Window");

        /** Create a new rendering window.
         *  @param strName Name of the window.
         *  @param nWidth Window width (pixels).
         *  @param nHeight Window height (pixels).
         *  @param nColourDepth Colour depth.
         *  @param bFullScreen Fullscreen mode.
         *  @return A pointer to the created window.
         */
        virtual IRenderWindow* CreateRenderWindow(const String &strName, uint nWidth, uint nHeight, uint nColourDepth, bool bFullScreen) = 0;
       
        /** Restart the renderer.
         */
        virtual void Reinitialise(void) = 0;

        /** Shutdown the renderer and cleanup resources.
         */
        virtual void Shutdown(void);

        /** Attaches the passed render target to the render system.
         */
        void AttachRenderTarget( IRenderTarget& target , bool bIsWindow = true);

        /** Attaches a IRenderApp interface to render window.
         *  @param pRenderApp Interface for render of the window.
         */
        bool SetRenderApp(IRenderApp* pRenderApp) 
        { 
            if (m_pRenderWindow == NULL) return false;
            m_pRenderWindow->SetRenderApp(pRenderApp); 
            return true;
        }

        /** Attaches a IRenderApp interface to a specified render target.
         *  @param pRenderApp Interface for render of the target.
         *  @param strTargetName Name of the target.
         *  @return False if that render target cannot be found.
         */
        bool SetRenderApp(IRenderApp* pRenderApp, String& strTargetName);

        /** Gets the capabilities of the render system. 
         */
		const CRenderSystemCapabilities* GetCapabilities(void) const 
        { 
            return m_pCapabilities; 
        }

        /** Start a scene render.
         */
        virtual void BeginScene(void) const = 0;

        /** End a scene render.
         */
        virtual void EndScene(void) const = 0;

        /** Print information on the API.
         */
        virtual void PrintInfo(void) const = 0;

        /** Initialise all render targets attached to this rendering system. 
         */
        void InitAllRenderTargets(void);

        /** Method for updating all render targets attached to this rendering system. 
         */
        void UpdateAllRenderTargets(void);

        /** Destroys a render target.
         *  @param strName Name of the target.
         */
        void DestroyRenderTarget(const String& strName);

        /** Detaches the render target with the passed name from the render system.
         *  @param strName Name of the target.
         *  @return Pointer on the target, NULL if that render target cannot be found.
         */
        IRenderTarget* DetachRenderTarget( const String &strName );

        /** Returns a pointer to the render target with the passed name.
         *  @param strName Name of the target.
         *  @return Pointer on the target, NULL if that render target cannot be found.
         */
        IRenderTarget* GetRenderTarget( const String &strName );

        /** Create a vertex buffer.
         *  @param nSize Elements number.
         *  @param nStride One element size.
         *  @param nFlags Creation options.
         *  @return Pointer on the new vertex buffer.
         */
        virtual IBufferBase* _CreateVB(ulong nSize, ulong nStride, ulong nFlags) const = 0;

        /** Create a index buffer.
         *  @param nSize Elements number.
         *  @param nStride One element size.
         *  @param nFlags Creation options.
         *  @return Pointer on the new index buffer.
         */
        virtual IBufferBase* _CreateIB(ulong nSize, ulong nStride, ulong nFlags) const = 0;

        /** Create a vertex declaration.
         *  @param pElements Declaration description.
         *  @param nCount Number of elements in the array.
         *  @return Pointer on the new declaration.
         */
        virtual IDeclaration* CreateDeclaration(const TDeclarationElement* pElements, size_t nCount) const = 0;

        /** Change the current vertex buffer.
         *  @param nStream Stream.
         *  @param pBuffer Pointer on the buffer.
         *  @param nStride Size of one vertex.
         *  @param nMinVertex Minimum vertex index.
         *  @param nMaxVertex Maximum vertex index.
         */
        virtual void _SetVB(uint nStream, const IBufferBase* pBuffer, ulong nStride, ulong nMinVertex, ulong nMaxVertex) = 0;

        /** Change the current index buffer.
         *  @param pBuffer Pointer on the buffer.
         *  @param nStride Size of one index.
         */
        virtual void _SetIB(const IBufferBase* pBuffer, ulong nStride) = 0;

        /** Change the current vertex declaration.
         *  @param pDeclaration The new declaration.
         */
        virtual void SetDeclaration(const IDeclaration* pDeclaration) = 0;

        /** Draw primitives.
         *  @param Type Primitive type.
         *  @param nFirstVertex First vertex position.
         *  @param nCount Number of triangles.
         */
        virtual void DrawPrimitives(TPrimitiveType Type, ulong nFirstVertex, ulong nCount) const = 0;

        /** Draw indexed primitives.
         *  @param Type Primitive type.
         *  @param nFirstIndex First index position.
         *  @param nCount Number of triangles.
         */
        virtual void DrawIndexedPrimitives(TPrimitiveType Type, ulong nFirstIndex, ulong nCount) const = 0;

        /** Push the current matix.
         *  @param Type Matrix type.
         */
        virtual void PushMatrix(TMatrixType Type) = 0;

        /** Pop the current matix.
         *  @param Type Matrix type.
         */
        virtual void PopMatrix(TMatrixType Type) = 0;

        /** Load a matrix.
         *  @param Type Matrix type.
         *  @param Matrix New matrix.
         */
        virtual void LoadMatrix(TMatrixType Type, const CMatrix4& Matrix) = 0;

        /** Load a matrix by multiplying it with the previous one.
         *  @param Type Matrix type.
         *  @param Matrix New matrix.
         */
        virtual void LoadMatrixMult(TMatrixType Type, const CMatrix4& Matrix) = 0;

        /** Get the current matrix.
         *  @param Type Matrix type.
         *  @param Matrix Destination matrix.
         */
        virtual void GetMatrix(TMatrixType Type, CMatrix4& Matrix) const = 0;

        /** Converts a color in the format taken care by the API.
         *  @param Color Color to be converted.
         *  @return Colour converted in the form of unsigned long.
         */
        virtual ulong ConvertColor(const CColor& Color) const = 0;

        /** Create a vertex buffer.
         *  @param nSize Elements number.
         *  @param nFlags Creation options.
         *  @param pData Pointer on the data.
         *  @return The new vertex buffer.
         */
        template <class T> CBuffer<T> CreateVertexBuffer(ulong nSize, ulong nFlags, const T* pData = NULL) const;

        /** Create a index buffer.
         *  @param nSize Elements number.
         *  @param nFlags Creation options.
         *  @param pData Pointer on the data.
         *  @return The new index buffer.
         */
        template <class T> CBuffer<T> CreateIndexBuffer(ulong nSize, ulong nFlags, const T* pData = NULL) const;

        /** Change the current vertex buffer.
         *  @param nStream Stream.
         *  @param Buffer Buffer.
         *  @param nMinVertex Minimum vertex index.
         *  @param nMaxVertex Maximum vertex index.
         */
        template <class T> void SetVertexBuffer(uint nStream, const CBuffer<T>& Buffer, ulong nMinVertex = 0, ulong nMaxVertex = 0);

        /** Change the current index buffer.
         *  @param Buffer Buffer.
         */
        template <class T> void SetIndexBuffer(const CBuffer<T>& Buffer);

        /** Create a vertex declaration.
         *  @param Elements Declaration description.
         *  @return Pointer on the new declaration.
         */
        template <size_t N> IDeclaration* CreateVertexDeclaration(const TDeclarationElement (&Elements)[N]) const;

        /** Create a new texture.
         *  @param Size Texture size.
         *  @param Format Pixels format.
         *  @param nFlags Creation options.
         *  @return Pointer on the created texture.
         */
        virtual ITextureBase* CreateTexture(const TVector2I& Size, TPixelFormat Format, ulong nFlags = 0) const = 0;
        
        /** Change a texture.
         *  @param nUnit Texture unit.
         *  @param pTexture The new texture.
         */
        virtual void SetTexture(uint nUnit, const ITextureBase* pTexture) const = 0;

        /** Initialize the config options of the render system.
         */
        virtual void InitConfigOptions(void);

        /** Change an config option.
         */
        virtual void SetConfigOption(const String& strName, const String& strValue);

        /** Get the config options map.
         */
        virtual ConfigOptionMap GetConfigOptions()
        { return m_Options; }

        /** Setup the alpha-blending.
         *  @param Src Source parameter.
         *  @param Dest Destination parameter.
         */
        virtual void SetupAlphaBlending(TBlend Src, TBlend Dest) const = 0;

        /** Setup the operations of one texture unit.
         *  @param nUnit Texture unit.
         *  @param Op Operator.
         *  @param Arg1 First argument.
         *  @param Arg2 Second argument.
         *  @param Constant Constant in the case of one argument is TXA_CONSTANT.
         */
        virtual void SetupTextureUnit(uint nUnit, TTextureOp Op, TTextureArg Arg1, TTextureArg Arg2 = TXA_DIFFUSE, const CColor& Constant = 0x00) const = 0;

        /** Enable/Disable a render parameter.
         *  @param Param Render parameter.
         *  @param bValue Value (on/off).
         */
        virtual void Enable(TRenderParameter Param, bool bValue) const = 0;

		/** Return the profile of shader to be used.
		 *	@param Type Shader type.
		 *	@return Profile corresponding to the type of shader.
		 */
        CGprofile GetShaderProfile(TShaderType Type) const;

		/** Return the options to be used for the creation of shaders
		 *	@param Type Shader type.
		 *	@return Options corresponding to the type of shader.
		 */
        const char* const* GetShaderOptions(TShaderType Type) const;

		/** Create a shader from a program Cg.
		 *	@param Program Cg program.
		 *	@param Type Shader type.
		 *	@return Created and loaded shader.
		 */
        virtual IShaderBase* CreateShader(CGprogram Program, TShaderType Type) const = 0;

		/** Change the current vertex shader.
		 *	@param pShader New shader.
		 */
        virtual void SetVertexShader(const IShaderBase* pShader) = 0;

        /** Change the current pixel shader.
		 *	@param pShader New shader.
		 */
        virtual void SetPixelShader(const IShaderBase* pShader) = 0;

		/** Change the current clear color.
		 *	@param Color New clear color.
		 */
        virtual void SetClearColor(const CColor& Color) = 0;

    protected :

        //! Pointer on the render window using this render system (only one)
        IRenderWindow* m_pRenderWindow;

        //! The render targets using this render system
        RenderTargetMap m_RenderTargets;

        //! Used to store the capabilities of the graphics card
        CRenderSystemCapabilities* m_pCapabilities;

        //! Stored options
	    ConfigOptionMap m_Options;

		//! Vertex shaders profile
        CGprofile   m_VSProfile;
		//! Pixel shaders profile
        CGprofile   m_PSProfile;
		//! Vertex shaders options
        const char* m_VSOptions[2];
		//! Pixel shaders options
        const char* m_PSOptions[2];

		//! Clear color (background color)
		CColor m_ClearColor; //@todo Modifier la place de cette variable
    };


    //=======================================================================
    // IRenderSystem inline functions
    //=======================================================================
    
    //-----------------------------------------------------------------------
    template <class T>
    inline CBuffer<T> IRenderSystem::CreateVertexBuffer(ulong nSize, ulong nFlags, const T* pData) const
    {
        CBuffer<T> Buffer(_CreateVB(nSize, sizeof(T), nFlags));
        if (pData)
            Buffer.Fill(pData, nSize);

        return Buffer;
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline CBuffer<T> IRenderSystem::CreateIndexBuffer(ulong nSize, ulong nFlags, const T* pData) const
    {
        CBuffer<T> Buffer(_CreateIB(nSize, sizeof(T), nFlags));
        if (pData)
            Buffer.Fill(pData, nSize);

        return Buffer;
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline void IRenderSystem::SetVertexBuffer(uint nStream, const CBuffer<T>& Buffer, ulong nMinVertex, ulong nMaxVertex)
    {
        _SetVB(nStream, Buffer.GetBuffer(), sizeof(T), nMinVertex, nMaxVertex ? nMaxVertex : Buffer.GetCount() - 1);
    }

    //-----------------------------------------------------------------------
    template <class T>
    inline void IRenderSystem::SetIndexBuffer(const CBuffer<T>& Buffer)
    {
        _SetIB(Buffer.GetBuffer(), sizeof(T));
    }

    //-----------------------------------------------------------------------
    template <size_t N>
    IDeclaration* IRenderSystem::CreateVertexDeclaration(const TDeclarationElement (&Elements)[N]) const
    {
        return CreateDeclaration(Elements, N);
    }

} // namespace Keos

#endif // KEOS_RENDERSYSTEM_H
