//------------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.Com, Inc.
//------------------------------------------------------------------------------

#ifndef _GDYNAMICTEXTURE_H_
#define _GDYNAMICTEXTURE_H_

#include "dgl/gTexManager.h"
#include "math/mRect.h"
#include "core/tVector.h"

// Forward definitions
class GuiControl;

extern void dynamicTextureCB(const U32 eventCode, void *userData);

class DynamicTexture
{
   friend void dynamicTextureCB(const U32 eventCode, void *userData);
private:
   TextureHandle *mTextureHandle;   ///< TextureHandle
   Point2I mSize;
   S32 mTexCBHandle;
   RectI mUpdateRect;               ///< When you call update() this is the rect, in screen coordinates, that gets grabbed
   RectF mTextureCoords;            ///< This is the texturecoord information you need to know to create texturecoords
                                    ///< if you are not using dglDrawBitmap
   bool mRTT;                       ///< Supports RTT emulation for Gui @see renderGuiControl
   bool mHasUpdateRect;             ///< Lets this have a default constructor

   GuiControl *mGuiControl;         ///< If the object is registered as wanting a Gui update every frame, this is
                                    ///< the variable that contains the Gui control it will render

   DynamicTexture *mTempDynamicTexture; ///< For RTT emulation

   typedef Vector<DynamicTexture *>::iterator RegisteredUpdateItr;
   static Vector<DynamicTexture *> smRegisteredGuiUpdaters; ///< This is a vector of DynamicTexture objects that
                                                            ///< will be updated every frame with their mGuiControl
                                                            ///< rendered to them.
   static Vector<DynamicTexture *> smRegisteredScreenUpdaters;

   static Vector<DynamicTexture *> smUpdateAtEndOfFrame;

   /// Helper function for the constructors
   void initDT();

public:
   /// Default Constructor
   DynamicTexture();

   /// Constructor
   ///
   /// @param  updateRect    The screen-rect this will grab from the color-buffer when update() is called
   DynamicTexture( const RectI &updateRect );

   /// Gui updater Constructor
   ///
   /// @param  control       Gui control to render to this texture every frame
   DynamicTexture( GuiControl *control );

   /// Destructor
   ~DynamicTexture();

   /// Grabs the rectangle defined by mUpdateRect from the color-buffer
   /// and sticks it in the texture
   void update();

   /// @defgroup dyntex_guiupdate Automatic Gui Updates
   /// This collection of functions is used to make textures which automatically
   /// update every frame based on the content of a GuiControl
   /// @{

   /// This registers this DynamicTexture to be updated every frame with the contents
   /// of a GuiControl
   ///
   /// @param  control     Gui control to render to this texture every frame
   void registerForGuiUpdates( GuiControl *control );

   /// This will unregister this DynamicTexture from per-frame Gui updates
   ///
   /// @returns True if it removed this from updating
   bool unregisterForGuiUpdates();

   /// This method is called in GuiCanvas before the screen-rendering takes place
   /// to update all of the registered GUI update textures
   static void updateGuiTextures();

   /// @}

   /// @defgroup dyntex_screenupdate Automatic Screen Updates
   /// This collection of functions is used to make tuextures which automatically
   /// update every frame based on a absolute set of screen coordinates
   /// @{

   /// This will register this DynamicTexture to be updated every frame
   void registerForScreenUpdates();

   /// This will unregister this control from screen updates
   ///
   /// @returns True if it removed this from updating
   bool unregisterForScreenUpdates();

   /// This method is called in GuiCanvas right before the buffers are swapped
   static void updateScreenTextures();

   /// This method just registers this texture to be updated at the end of the
   /// current frame
   void updateAtEndOfFrame();

   /// This method will update all the textures that asked to be rendered at the
   /// end of the current frame
   static void updateEndOfFrameTextures();

   /// @}

   /// This lets you change the rect that this texture grabs from the screen
   /// This is not a cheap operation, you don't want to do it unless
   /// you have a good reason to.
   ///
   /// @param   newUpdatePoint   The new rect, in screen coordinates, to use for the update rect
   void setUpdateRect( const RectI &newRect );

   /// @defgroup dyntex_rtt Render To Texture Simulation
   /// Render To Texture is really great because it allows you to draw to offscreen
   /// buffers and not muck around with copying pixels etc. Unfortunatly there is
   /// no way to do this cross-platform (that I know of) in OpenGL 1.5 so this
   /// is the work around. When the time comes to draw something to a texture
   /// we will copy the current contents of that area of the color buffer into
   /// a temporary DynamicTexture, draw (to that area), grab those pixels, then
   /// restore the contents of the color buffer to what it was before we drew
   /// to it. This method is NOT necesasary for every application of DynamicTexture.
   /// In fact, most applications will not need it at all.
   /// @{

   /// Manually enable or disable RTT mode
   void setEnableRTT( bool enable = true );

   /// Stores the pixels defined by the updateRect into a temporary
   /// DynamicTexture object so that they can be restored at a later
   /// time.
   void storePixels();

   /// Restores the pixels in the temporary DynamicTexture object
   /// by doing a dglDrawBitmap with them and drawing them, stretched
   /// to the updateRect.
   void restorePixels();

   /// @}

   /// Returns the texture handle for this so it can be used to render
   ///
   /// @return Texture handle for the dynamic texture
   TextureHandle getTextureHandle() const;

   /// Returns a const pointer to the texture coordinate information
   ///
   /// @return Texture coordinate information
   const RectF * const texCoordInfo() const;


   /// Returns the update rect of this dynamic texture
   ///
   /// @return Update rect for the dynamic texture
   const RectI * const getUpdateRect() const;


   /// Returns the GuiControl this texture is using, if any
   GuiControl *getGuiControl() const;

   /// Renders a GuiControl, and it's children, to the texture
   /// This has two different behaviors depending on if RTT emulation
   /// is specified as enabled.
   ///
   /// In OpenGL, rendering to an off screen
   /// buffer is a platform-specific operation and I don't have a Mac
   /// to make sure this works and what have you, so what happens is,
   /// if RTT mode is turned on, the area that will be used to draw
   /// the texture will be saved, with the temp DyanmicTexture object
   /// then the rendering will take place, it will grab the texture,
   /// then restore the edited area of the color-buffer.
   ///
   /// If RTT is turned off, then it will draw the control on the screen,
   /// grab it, and leave it. This is not as useless as it seems, this is
   /// not at all a bad way to do things, just do it all in the GuiCanvas
   /// before it begins rendering the content control
   /// @see GuiCanvas::renderFrame
   ///
   /// @param   ctrl   GuiControl to render
   void renderGuiControl( GuiControl *ctrl = NULL, bool rttMode = false );
};


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

inline TextureHandle DynamicTexture::getTextureHandle() const
{
   return *mTextureHandle;
}

inline const RectF * const DynamicTexture::texCoordInfo() const
{
   return &mTextureCoords;
}

inline GuiControl *DynamicTexture::getGuiControl() const
{
   return mGuiControl;
}

inline const RectI * const DynamicTexture::getUpdateRect() const
{
   return &mUpdateRect;
}

inline void DynamicTexture::updateAtEndOfFrame()
{
   smUpdateAtEndOfFrame.push_back( this );
}

inline void DynamicTexture::initDT()
{
   mTexCBHandle = TextureManager::registerEventCallback(dynamicTextureCB, this);
   mTextureHandle = NULL;
   mRTT = false;
   mHasUpdateRect = false;
   mGuiControl = NULL;
   mTempDynamicTexture = NULL;
}

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

#endif