//------------------------------------------------------------------------------
// Vector Field
//------------------------------------------------------------------------------

#ifndef _GVECTORFIELD_H_
#define _GVECTORFIELD_H_

#include "math/mPoint.h"
#include "core/stream.h"

// Comment this line out to disable the ability to render out the field
#define ENABLE_FIELD_VISUALIZE

/// Function pointer that is used for functions to init vector fields
/// @note This function will be passed the normal texture coordinates in outVec
typedef void(FN_CDECL *VectorFieldInitFn)( const int x, const int y, const Point2I &resolution, const F32 maxX, const F32 maxY, Point2F *outVec );

class VectorField
{

private:
   Point2I mFieldResolution;  ///< Resolution of the vector field, x * y = number of quads
   Point2F *mVectorField;     ///< Vector Field

   Point2F *mFieldVerts;      ///< Vertices for rendering the field
   U16     *mFieldIndices;    ///< Indices for rendering the field

   Point2F mMax;              ///< Maximum texture coordinate values

#ifdef ENABLE_FIELD_VISUALIZE
   Point2F *mUnflippedVecField;  ///< If the coords get flipped, then this will be unflipped
#endif

   /// Helper function to create the vector field
   ///
   /// @param  resolution     Vector Field resolution
   void allocVectorField( const Point2I &resolution );

   /// Helper function to destroy the vector field
   void destroyVectorField();

   /// Helper function to null the pointers
   void nullField();

   /// Helper inline just so I can store the vector field as a 1d array but access
   /// it as a 2d array
   Point2F &getVector( const U32 x, const U32 y );
   const Point2F &getVector( const U32 x, const U32 y ) const;

   /// Put this field out to a stream
   void serialize( Stream *stream ) const;

   /// Read a stream and set up this field
   void unserialize( Stream *stream );

public:
   /// This is used to specify flip options for creating the texture coord array
   enum TextureCoordFlip
   {
      Flip_None = 0,
      Flip_X    = BIT(0),
      Flip_Y    = BIT(1),
      Flip_XY   = Flip_X | Flip_Y
   };

   /// Default Constructor
   VectorField();

   /// Constructor
   ///
   /// @param  resolution     Vector Field resolution
   /// @param  maxX           Maximum value that can be assigned to an x-coord
   /// @param  maxY           Maximum value that can be assigned to a y-coord
   /// @param  flip           Flipping options for the texture coordinates
   /// @param  initFn         Function pointer to init the vector field values with
   VectorField( const Point2I &resolution, const F32 maxX = 1.f, const F32 maxY = 1.f,
                TextureCoordFlip flip = Flip_None, VectorFieldInitFn initFn = NULL );

   /// Constructor with file name
   ///
   /// @param  fileName       File name to load from
   VectorField( const char *fileName );

   /// Destructor
   ~VectorField();

   /// Init the vector field
   /// @param  maxX           Maximum value that can be assigned to an x-coord
   /// @param  maxY           Maximum value that can be assigned to a y-coord
   /// @param  flip           Flipping options for the texture coordinates
   /// @param  initFn         Function pointer to init the vector field values with
   void initVectorField( F32 maxX = 1.f, F32 maxY = 1.f, TextureCoordFlip flip = Flip_None, VectorFieldInitFn initFn = NULL );

   /// Load the vector field from a file
   /// @see saveField
   ///
   /// @param  fileName       File name to load from
   bool loadField( const char *fileName );

   /// Save the vector field to a file
   ///
   /// @param fileName        File name to save to
   bool saveField( const char *fileName ) const;

   /// Render out the vector field
   ///
   /// @param  texture        True if this should enable texturing
   void renderField( bool texture = false ) const;

   /// Render the field to let someone visualize what it is doing
   void visualizeField( F32 alpha = 1.f ) const;
};

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

inline Point2F &VectorField::getVector( const U32 x, const U32 y )
{
   AssertFatal( x < mFieldResolution.x && y < mFieldResolution.y, "Vector Field access out of bounds" );

   return mVectorField[x + y * mFieldResolution.x];
}

inline const Point2F &VectorField::getVector( const U32 x, const U32 y ) const
{
   AssertFatal( x < mFieldResolution.x && y < mFieldResolution.y, "Vector Field access out of bounds" );

   return mVectorField[x + y * mFieldResolution.x];
}

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

#ifndef ENABLE_FIELD_VISUALIZE
inline void VectorField::visualizeField( F32 alpha /* = 0.5f */ ) const
{
}
#endif

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

inline void VectorField::nullField()
{
#ifdef ENABLE_FIELD_VISUALIZE
   mUnflippedVecField = NULL;
#endif
   mFieldVerts = NULL;
   mFieldIndices = NULL;
   mVectorField = NULL;
}

#endif