/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2017 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/

#include "OgreHlmsDatablock.h"
#include "OgreConstBufferPool.h"
#include "OgreTextureGpuListener.h"

#include "OgreHeaderPrefix.h"

namespace Ogre
{
    /** \addtogroup Core
    *  @{
    */
    /** \addtogroup Resources
    *  @{
    */

    /** This is not a regular header, therefore it has no include guards.
        This header contains a base class that defines
        common functionality across multiple Hlms datablock implementations that need
        textures. However C++ does not let us statically define the size of
        mTexIndices, mTextures & mSamplerblocks array; we would have to either force the
        same maximum to all implementations, or use the heap.
    @par
        Therefore this header uses macros to customize each base class for each implementations.
        DO NOT INCLUDE THIS HEADER DIRECTLY.

        The way to include this header is by doing:

        @code
        #define _OgreHlmsTextureBaseClassExport _OgreHlmsUnlitExport
        #define OGRE_HLMS_TEXTURE_BASE_CLASS HlmsUnlitBaseTextureDatablock
        #define OGRE_HLMS_TEXTURE_BASE_MAX_TEX NUM_UNLIT_TEXTURE_TYPES
        #define OGRE_HLMS_CREATOR_CLASS HlmsUnlit
            #include "OgreHlmsTextureBaseClass.h"
        #undef _OgreHlmsTextureBaseClassExport
        #undef OGRE_HLMS_TEXTURE_BASE_CLASS
        #undef OGRE_HLMS_TEXTURE_BASE_MAX_TEX
        #undef OGRE_HLMS_CREATOR_CLASS
        @endcode

        Where all the necessary macros are defined prior to including this header,
        and once we're done, we undef these macros.

        When OGRE_HLMS_TEXTURE_BASE_CLASS is not defined, the *.inl version of this
        file will include a few headers in order to get proper syntax highlighting.
    */
    class _OgreHlmsTextureBaseClassExport OGRE_HLMS_TEXTURE_BASE_CLASS :
            public HlmsDatablock, public ConstBufferPoolUser, public TextureGpuListener
    {
    protected:
        /// The last bit in mTexIndices (ManualTexIndexBit) is reserved. When set, it
        /// indicates that mTexIndices was set manually, rather than being automatically
        /// set to texture[i]->getInternalSliceStart()
        uint16 mTexIndices[OGRE_HLMS_TEXTURE_BASE_MAX_TEX];

        DescriptorSetTexture const *mTexturesDescSet;
        DescriptorSetSampler const *mSamplersDescSet;

        TextureGpu              *mTextures[OGRE_HLMS_TEXTURE_BASE_MAX_TEX];
        HlmsSamplerblock const	*mSamplerblocks[OGRE_HLMS_TEXTURE_BASE_MAX_TEX];

        uint8                   mTexLocationInDescSet[OGRE_HLMS_TEXTURE_BASE_MAX_TEX];

        void scheduleConstBufferUpdate( bool updateTextures=false, bool updateSamplers=false );
        void updateDescriptorSets( bool textureSetDirty, bool samplerSetDirty );

        /// Expects caller to call flushRenderables if we return true.
        virtual bool bakeTextures( bool hasSeparateSamplers );
        /// Expects caller to call flushRenderables if we return true.
        bool bakeSamplers(void);

        virtual void cloneImpl( HlmsDatablock *datablock ) const;

    public:
        OGRE_HLMS_TEXTURE_BASE_CLASS( IdString name, Hlms *creator,
                                      const HlmsMacroblock *macroblock,
                                      const HlmsBlendblock *blendblock,
                                      const HlmsParamVec &params );
        virtual ~OGRE_HLMS_TEXTURE_BASE_CLASS();

        virtual void saveTextures( const String &folderPath, set<String>::type &savedTextures,
                                   bool saveOitd, bool saveOriginal,
                                   HlmsTextureExportListener *listener );

        /** Sets a new texture for rendering. Calling this function may trigger an
            HlmsDatablock::flushRenderables if the texture or the samplerblock changes.
            Might not be called if old and new texture belong to the same TexturePool.
        @param texType
            Texture unit. Must be in range [0; OGRE_HLMS_TEXTURE_BASE_MAX_TEX)
        @param texture
            Texture to change to. If it is null and previously wasn't (or viceversa), will
            trigger HlmsDatablock::flushRenderables.
        @param refParams
            Optional. We'll create (or retrieve an existing) samplerblock based on the input parameters.
            When null, we leave the previously set samplerblock (if a texture is being set, and if no
            samplerblock was set, we'll create a default one)
        @param sliceIdx
            Optional. When not set to 0xFFFF, it means you want to explicitly set the texture array
            index, instead of relying on texture->getInternalSliceStart().
            Only useful if texture is TextureTypes::Type2DArray. For advanced users.
        */
        void setTexture( uint8 texType, TextureGpu *texture, const HlmsSamplerblock *refParams=0,
                         uint16 sliceIdx=std::numeric_limits<uint16>::max() );
        TextureGpu* getTexture( uint8 texType ) const;

        /// Same as setTexture, but samplerblockPtr is a raw samplerblock retrieved from HlmsManager,
        /// and is assumed to have its reference count already be incremented for us
        /// (note HlmsManager::getSamplerblock() already increments the ref. count).
        /// Mostly for internal use, but can speed up loading if you already manage samplerblocks
        /// manually and have the raw ptr.
        void _setTexture( uint8 texType, TextureGpu *texture,
                          const HlmsSamplerblock *samplerblockPtr=0,
                          uint16 sliceIdx=std::numeric_limits<uint16>::max() );

        /** Sets a new sampler block to be associated with the texture
            (i.e. filtering mode, addressing modes, etc). If the samplerblock changes,
            this function will always trigger a HlmsDatablock::flushRenderables
        @param texType
            Texture unit. Must be in range [0; OGRE_HLMS_TEXTURE_BASE_MAX_TEX)
        @param params
            The sampler block to use as reference.
        */
        void setSamplerblock( uint8 texType, const HlmsSamplerblock &params );
        const HlmsSamplerblock* getSamplerblock( uint8 texType ) const;

        /// Same as setSamplerblock, but samplerblockPtr is a raw samplerblock retrieved from
        /// HlmsManager, and is assumed to have its reference count already be incremented for us.
        /// See _setTexture.
        void _setSamplerblock( uint8 texType, const HlmsSamplerblock *samplerblockPtr );

        /// This function has O( log N ) complexity, but O(1) if the texture was not set.
        uint8 getIndexToDescriptorTexture( uint8 texType );
        /// Do not call this function if RSC_SEPARATE_SAMPLERS_FROM_TEXTURES is not set.
        /// If not set, then just the result value from getIndexToDescriptorTexture
        /// instead. Same complexity as getIndexToDescriptorTexture
        uint8 getIndexToDescriptorSampler( uint8 texType );

        virtual void notifyTextureChanged( TextureGpu *texture, TextureGpuListener::Reason reason,
                                           void *extraData );
        virtual bool shouldStayLoaded( TextureGpu *texture );

        void loadAllTextures(void);
    };
}

#include "OgreHeaderSuffix.h"
