//-----------------------------------------------------------------------------
// Copyright (c) 2012 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 _RENDERPASSMANAGER_H_
#define _RENDERPASSMANAGER_H_

#ifndef _GFXDEVICE_H_
#include "gfx/gfxDevice.h"
#endif
#ifndef _SCENEOBJECT_H_
#include "scene/sceneObject.h"
#endif
#ifndef _SIMOBJECT_H_
#include "console/simObject.h"
#endif
#ifndef _DATACHUNKER_H_
#include "core/dataChunker.h"
#endif
#ifndef _SCENEMANAGER_H_
#include "scene/sceneManager.h"
#endif
#ifndef _SCENEMANAGER_H_
#include "scene/sceneManager.h"
#endif
#ifndef _CUBEMAPDATA_H_
#include "gfx/sim/cubemapData.h"
#endif
#ifndef _GFXPRIMITIVEBUFFER_H_
#include "gfx/gfxPrimitiveBuffer.h"
#endif

class SceneRenderState;
class ISceneObject;
class BaseMatInstance;
struct SceneData;
class ShaderData;
class RenderBinManager;
class LightInfo;
struct RenderInst;
class MatrixSet;
class GFXPrimitiveBufferHandle;
class CustomShaderBindingData;

/// A RenderInstType hash value.
typedef U32 RenderInstTypeHash;

/// A a tiny wrapper around String that exposes a U32 operator so
/// that we can assign the RIT to RenderInst::type field.
class RenderInstType
{
   /// For direct access to mName.
   friend class RenderBinManager;

protected:
      
   String mName;

public:  

   RenderInstType()
      :  mName( Invalid.mName )
   {
   }

   RenderInstType( const RenderInstType &type )
      :  mName( type.mName )
   {
   }

   RenderInstType( const String &name )
      :  mName( name )
   {
   }

   ~RenderInstType() {}

   operator RenderInstTypeHash() const { return (RenderInstTypeHash)mName.getHashCaseInsensitive(); }

   const String& getName() const { return mName; }

   bool isValid() const { return (RenderInstTypeHash)*this != (RenderInstTypeHash)Invalid; }

   static const RenderInstType Invalid;
};


///
class RenderPassManager : public SimObject
{
   typedef SimObject Parent;

public:   

   // Default bin types.  Not necessarily the only bin types in the system.
   // RIT = "R"ender "I"nstance "T"ype
   static const RenderInstType RIT_Mesh;
   static const RenderInstType RIT_Shadow;
   static const RenderInstType RIT_Sky;
   static const RenderInstType RIT_Terrain;
   static const RenderInstType RIT_Object;   // objects that do their own rendering
   static const RenderInstType RIT_ObjectTranslucent;// self rendering; but sorted with static const RenderInstType RIT_Translucent
   static const RenderInstType RIT_Decal;
   static const RenderInstType RIT_DecalRoad;
   static const RenderInstType RIT_Water;
   static const RenderInstType RIT_Foliage;
   static const RenderInstType RIT_VolumetricFog;
   static const RenderInstType RIT_Translucent;
   static const RenderInstType RIT_Begin;
   static const RenderInstType RIT_Custom;
   static const RenderInstType RIT_Particle;
   static const RenderInstType RIT_Occluder;
   static const RenderInstType RIT_Editor;
   static const RenderInstType RIT_Probes;

public:

   RenderPassManager();
   virtual ~RenderPassManager();

   /// @name Allocation interface
   /// @{

   /// Allocate a render instance, use like so:  MyRenderInstType* t = gRenderInstMgr->allocInst<MyRenderInstType>();
   /// Valid until ::clear called.
   template <typename T>
   T* allocInst()
   {
      T* inst = mChunker.alloc<T>();
      inst->clear();
      return inst;
   }

   /// Allocate a matrix, valid until ::clear called.
   MatrixF* allocUniqueXform(const MatrixF& data) 
   { 
      MatrixF *r = mChunker.alloc<MatrixF>(); 
      *r = data; 
      return r; 
   }

   enum SharedTransformType
   {
      View,
      Projection,
   };

   const MatrixF* allocSharedXform(SharedTransformType stt);

   void assignSharedXform(SharedTransformType stt, const MatrixF &xfm);

   MatrixSet &getMatrixSet() { return *mMatrixSet; }

   /// Allocate a GFXPrimitive object which will remain valid 
   /// until the pass manager is cleared.
   GFXPrimitive* allocPrim() { return mChunker.alloc<GFXPrimitive>(); }
   /// @}

   /// Add a RenderInstance to the list
   virtual void addInst( RenderInst *inst );
   
   /// Sorts the list of RenderInst's per bin. (Normally, one should just call renderPass)
   void sort();

   /// Renders the list of RenderInsts (Normally, one should just call renderPass)
   void render( SceneRenderState *state );

   /// Resets our allocated RenderInstances and Matrices. (Normally, one should just call renderPass)
   void clear();

   // Calls sort, render, and clear
   void renderPass( SceneRenderState *state );

   /// Returns the active depth buffer for this pass (NOTE: This value may be GFXTextureTarget::sDefaultDepthStencil)
   GFXTextureObject *getDepthTargetTexture();

   /// Assigns the value for the above method
   void setDepthTargetTexture(GFXTextureObject *zTarget);

   /// @name RenderBinManager interface
   /// @{

   /// Add a render bin manager to the list of render bin manager, this SceneRenderPassManager now owns the render bin manager and will free it when needed.
   /// @param mgr Render manager to add
   /// @param processAddOrder Where to add the manager in the addInst list, set to NO_PROCESSADD to skip processing
   ///        this is in place for RenderManagers that will bypass the main ::addInst interface and doesn't want to process
   ///        them.
   /// @param renderOrder Where to add the manager in the render list.
   void addManager(RenderBinManager* mgr);
   
   /// Removes a manager from render and process add lists
   /// @param mgr Render bin manager to remove, the caller is now responsible for freeing the mgr.
   void removeManager(RenderBinManager* mgr);

   /// How many render bin managers do we have?
   U32 getManagerCount() const { return mRenderBins.size(); }

   /// Get the render manager at i
   RenderBinManager* getManager( S32 i ) const;

   /// @}

   /// Get scene manager which this render pass belongs to.
   SceneManager* getSceneManager()
   {
      if ( !mSceneManager )
         mSceneManager = gClientSceneGraph;

      return mSceneManager;
   }

   /// This signal is triggered when a render bin is about to be rendered.
   ///
   /// @param bin    The render bin we're signaling.
   /// @param state  The current scene state.
   /// @params preRender   If true it is before the bin is rendered, else its 
   ///                     after being rendered.
   ///
   typedef Signal <void (  RenderBinManager *bin, 
                           const SceneRenderState *state, 
                           bool preRender )> RenderBinEventSignal;

   /// @see RenderBinEventSignal
   static RenderBinEventSignal& getRenderBinSignal();


   typedef Signal<void(RenderInst *inst)> AddInstSignal;

   AddInstSignal& getAddSignal( RenderInstTypeHash type )
   {
      return mAddInstSignals.findOrInsert( type )->value; 
   }

   // ConsoleObject interface
   static void initPersistFields();
   DECLARE_CONOBJECT(RenderPassManager);

protected:

   MultiTypedChunker mChunker;
      
   Vector< RenderBinManager* > mRenderBins;


   typedef HashTable<RenderInstTypeHash,AddInstSignal> AddInstTable;

   AddInstTable mAddInstSignals;

   SceneManager * mSceneManager;
   GFXTexHandle mDepthBuff;
   MatrixSet *mMatrixSet;

   /// Do a sorted insert into a vector, renderOrder bool controls which test we run for insertion.
   void _insertSort(Vector<RenderBinManager*>& list, RenderBinManager* mgr, bool renderOrder);
};

//**************************************************************************
// Render Instance
//**************************************************************************
struct RenderInst
{
   /// The type of render instance this is.
   RenderInstTypeHash type;

   /// This should be true if the object needs to be sorted 
   /// back to front with other translucent instances.
   /// @see sortDistSq
   bool translucentSort;  

   /// The reference squared distance from the camera used for
   /// back to front sorting of the instances.
   /// @see translucentSort
   F32 sortDistSq;

   /// The default key used by render managers for
   /// internal sorting.
   U32 defaultKey;

   /// The secondary key used by render managers for
   /// internal sorting.
   U32 defaultKey2;

   /// Does a memset to clear the render instance.
   void clear();
};

struct ObjectRenderInst : public RenderInst
{
   /// This is a delegate specific index which is usually
   /// used to define a mounted object.
   S32 objectIndex;

   /// Extra data to be used within the render callback.
   /// ObjectRenderInst does not own or cleanup this data.
   void *userData;

   /// The delegate callback function to call to render
   /// this object instance.
   ///
   /// @param ri           The ObjectRenderInst that called the delegate.
   ///
   /// @param state        The scene state we're rendering.
   ///
   /// @param overrideMat  An alternative material to use during rendering... usually
   ///                     used for special renders like shadows.  If the object doesn't
   ///                     support override materials it shouldn't render at all.
   Delegate<void( ObjectRenderInst *ri, 
                  SceneRenderState *state, 
                  BaseMatInstance *overrideMat )> renderDelegate;

   // Clear this instance.
   void clear();
};

struct MeshRenderInst : public RenderInst
{
   ////
   GFXVertexBufferHandleBase *vertBuff;
   
   ////
   GFXPrimitiveBufferHandle *primBuff;

   /// If not NULL it is used to draw the primitive, else
   /// the primBuffIndex is used.
   /// @see primBuffIndex
   GFXPrimitive *prim;

   /// If prim is NULL then this index is used to draw the
   /// indexed primitive from the primitive buffer.
   /// @see prim
   U32 primBuffIndex;
   
   /// The material to setup when drawing this instance.
   BaseMatInstance *matInst;

   /// The object to world transform (world transform in most API's).
   const MatrixF *objectToWorld;       
   
   /// The worldToCamera (view transform in most API's).
   const MatrixF* worldToCamera;       
   
   /// The projection matrix.
   const MatrixF* projection;         

   // misc render states
   U8    transFlags;
   bool  reflective;
   F32   visibility;

   /// A generic hint value passed from the game
   /// code down to the material for use by shader 
   /// features.
   void *materialHint;

   /// The lights we pass to the material for this 
   /// mesh in order light importance.
   LightInfo* lights[8];

   // textures
   GFXTextureObject *lightmap;
   GFXTextureObject *fogTex;
   GFXTextureObject *backBuffTex;
   GFXTextureObject *reflectTex;
   GFXTextureObject *miscTex;
   GFXTextureObject *accuTex;
   GFXCubemap   *cubemap;

   /// @name Hardware Skinning
   /// {
   MatrixF *mNodeTransforms;
   U32 mNodeTransformCount;
   /// }

#ifdef TORQUE_ENABLE_GFXDEBUGEVENTS
   const char *meshName;
   const char *objectName;
#endif

   //Custom Shader data
   Vector<CustomShaderBindingData> mCustomShaderData;

   void clear();
};

enum ParticleSystemState
{
   PSS_AwaitingHighResDraw = 0, // Keep this as first element so that if the offscreen manager rejects a particle system it will get drawn high-res
   PSS_AwaitingOffscreenDraw,
   PSS_AwaitingCompositeDraw,
   PSS_AwaitingMixedResDraw,
   PSS_DrawComplete,
};

/// A special render instance for particles.
struct ParticleRenderInst : public RenderInst
{
   /// The vertex buffer.
   GFXVertexBufferHandleBase *vertBuff;
   
   /// The primitive buffer.
   GFXPrimitiveBufferHandle *primBuff;

   /// The total particle count to render.
   S32 count;

   bool glow;

   /// The combined model, camera, and projection transform.
   const MatrixF *modelViewProj;       
        
   /// Blend style for the particle system 
   enum BlendStyle {
      BlendUndefined = 0,
      BlendNormal,
      BlendAdditive,
      BlendSubtractive,
      BlendPremultAlpha,
      BlendGreyscale,
      BlendStyle_COUNT,
   };
   U8 blendStyle;

   /// For the offscreen particle manager
   U8 targetIndex;

   /// State for the particle system
   ParticleSystemState systemState;

   /// The soft particle fade distance in meters.
   F32 softnessDistance;

   /// Bounding box render transform
   const MatrixF *bbModelViewProj;

   Point3F wsPosition;

   /// The particle texture.
   GFXTextureObject *diffuseTex;

   void clear();
};

class GFXOcclusionQuery;
class SceneObject;

/// A special render instance for occlusion tests.
struct OccluderRenderInst : public RenderInst
{   
   Point3F scale;
   Point3F position;   
   const MatrixF *orientation;
   GFXOcclusionQuery *query; 
   
   // This optional query will have all pixels rendered.
   // Its purpose is to return to the user the full pixel count for comparison
   // with the other query.
   GFXOcclusionQuery *query2;

   /// Render a sphere or a box.
   bool isSphere;

   void clear();
};

#endif // _RENDERPASSMANAGER_H_
