//-----------------------------------------------------------------------------
// Copyright (c) 2013 GarageGames, LLC
//
// 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.
//-----------------------------------------------------------------------------

#ifndef _TEXTURE_HANDLE_H_
#define _TEXTURE_HANDLE_H_

#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif

#ifndef _PLATFORMGL_H_
#include "platform/platformAssert.h"
#include "platform/platformGL.h"
#endif

//-----------------------------------------------------------------------------

class GBitmap;
class TextureObject;

//------------------------------------------------------------------------------

/// This is the main texture manager interface.  Texturing can be
/// a bit complicated, but if you follow these easy steps, it is
/// really quite simple!
///
/// In order to use a texture on disk, first you must create a
/// TextureHandle data structure for it.
/// @code
/// TextureHandle handle = TextureHandle("pathToTexture", textureType);
/// @endcode
/// See the documentation on the different enumerated types for more info
/// on texture types.
///
/// Ok, now you have your texture loaded into video memory or ram,
/// whichever is chooses.  In order to tell OpenGL to use your texture,
/// you have to bind it.  GL_TEXTURE_2D is the type of texture you're
/// binding - a 2 dimisional texture.  Also note that you only have
/// to do this if you are using direct OpenGL commands to draw rather
/// than dgl.  Dgl manages the below on it's own so you don't have to worry about it.
/// @code
/// glBindTexture(GL_TEXTURE_2D, handle.getGLName());
/// @endcode
/// Now you can begin to draw you texture.  If you havn't already,
/// make sure you make a call to glEnable(GL_TEXTURE_2D);  before
/// you start drawing and a call to glDisable(GL_TEXTURE_2D); when
/// you're done.  Failure to call glEnable will cause the texture not
/// to draw, and failure to call glDisable will probably case
/// an assert in debug mode and ugly artifacts in release.
///
/// If you are going through dgl, all you need is the TextureHandle and
/// some points.  See the dgl documentation for more info on each
/// individual function in the dgl library.  However, most dgl functions
/// will take a TextureObject data type.  And, it just so happens that
/// a TextureHandle has a TextureObject!  It also has an
/// operator TextureObject*(), which lets you cast a TextureHandle to
/// a TextureObject.  That means that all you have to do is ignore
/// the TextureObject parameter and just give it a TextureHandle.
///
/// Some tips on texture performance:
///
/// Instead of using hard-coded paths, use a hook to a console variable.
/// You will probably change the directory structure for your game,
/// and that means that you will have to go back to all of the hardcoded
/// paths and change them by hand, then rebuild the engine.  It is much
/// better to use script variables since they are all in one place and
/// easier to change.
///
/// Add the path string for your texture to the StringTable.  Doing so
/// helps in string lookups and faster string performance.
///
/// Don't create the texture every frame if at all possible.  Make it
/// a global variable if you have to - just don't load every frame.
/// Loading data off of the disk every frame WILL cause massive
/// performance loss and should be avoided at all costs.  This is
/// not to mention the overhead of generating mip map levels
/// and uploading the texture into memory when the texture is created.
///
/// @note
/// Texture handles can be allocated in 2 ways - by name to be loaded
/// from disk, or by name to a dynamically generated texture
///
/// If you create a GBitmap and register it, the Texture manager
/// owns the pointer - so if you re-register a texture with the same
/// name, the texture manager will delete the second copy.
///
/// Also note the operator TextureObject*, as you can actually cast
/// a TextureHandle to a TextureObject* if necessary.
class TextureHandle
{    
public:
    enum TextureHandleType
    {
        // Invalid texture.
        InvalidTexture = 0,

        /// Bitmap that will be unloaded after texture has been uploaded.
        /// The bitmap will need reloading if the textures need to be restored.
        /// This is the preferred type.
        BitmapTexture = 100,

        /// Same as BitmapTexture except that the bitmap is kept which occupies main memory however
        /// it does not require loading if textures need to be restored.
        BitmapKeepTexture = 200,
    };

public:
    TextureHandle() : object( NULL ) {}

    TextureHandle( TextureObject *to )
    {
        object = to;
        lock();
    }

    TextureHandle( const TextureHandle &th ) 
    {
        object = th.object;
        lock();
    }

    TextureHandle(const char* textureKey, TextureHandleType type, bool clampToEdge = false, bool force16Bit = false );

    TextureHandle(const char* textureKey, GBitmap* bmp, TextureHandleType type, bool clampToEdge = false);

    ~TextureHandle() { unlock(); }

    TextureHandle& operator=(const TextureHandle &t) 
    {
        unlock();
        object = t.object;
        lock();
        return *this;
    }

    bool set(const char* pTextureKey, TextureHandleType type = BitmapTexture, bool clampToEdge = false, bool force16Bit = false );

    bool set(const char* pTextureKey, GBitmap *bmp, TextureHandleType type, bool clampToEdge = false);

    bool operator==( const TextureHandle& handle ) const { return handle.object == object; }

    bool operator!=( const TextureHandle& handle ) const { return handle.object != object; }

    void setClamp( const bool clamp );

    void setFilter( const GLuint filter );

    void clear( void ) { unlock(); }

    void refresh( void );

    operator TextureObject*() { return object; }
    inline bool NotNull( void ) const { return object != NULL; }
    inline bool IsNull( void ) const { return object == NULL; }
    const char* getTextureKey( void ) const;
    U32 getWidth( void ) const;
    U32 getHeight( void ) const;
    GBitmap* getBitmap( void );
    const GBitmap* getBitmap( void ) const;
    U32 getGLName( void ) const;

private:
    void lock( void );
    void unlock( void );

private:
    TextureObject *object;

};

//-----------------------------------------------------------------------------

extern TextureHandle BadTextureHandle;

#endif // _TEXTURE_HANDLE_H_