#ifndef __XGENMENTALRAYPROCEDURAL_H__
#define __XGENMENTALRAYPROCEDURAL_H__
#include <vector>
#include <shader.h>
#include <memory>
#include <XGen/XgRenderAPI.h>
using namespace XGenRenderAPI;
typedef miTag miString;
namespace XGenMR
{
    class ProceduralWrapper;
    struct miMatrixClass
    {
        miMatrix m;
    };
    struct miMatrixStruct
    {
        miMatrixStruct( const miMatrix m )
        {
            mi_matrix_copy( m_matrix, m );
        }
        miMatrixStruct( const miMatrixStruct& m )
        {
            *this = m;
        }
        miMatrixStruct& operator=( const miMatrixStruct& m )
        {
            mi_matrix_copy( m_matrix, m.m_matrix );
            return *this;
        }
        
        miMatrix m_matrix;
    };
    
    
    template< class T, class E, int N >
    class TUserData
    {
    public:
        TUserData( PrimitiveCache* pc, PrimitiveCache::EBoolAttribute bAdd, E eArray, int context, const char* name );
        TUserData( const T* p, int context, const char* name );
        inline T get(size_t j) const;
        inline void registerUserData( size_t& io_perPrim, size_t& io_perPoint, XGenMR::UserDataFormat& fmt, size_t& k, int* interpolateComponent );
        inline void registerUserData( size_t& io_perPrim, size_t& io_perPoint, XGenMR::UserDataFormat& fmt );
        inline void copy( size_t j, float* cur, size_t& k, int context );
        inline bool isValid( int context );
    private:
        int         m_context;
        bool        m_add;
        const T*    m_p;
        std::string m_name;
    };
    typedef TUserData<int,PrimitiveCache::EIntArrayAttribute,1> TIntUserData;
    typedef TUserData<float,PrimitiveCache::EFloatArrayAttribute,1> TFloatUserData;
    typedef TUserData<vec3,PrimitiveCache::EVec3ArrayAttribute,3> TVec3UserData;
    typedef std::vector< TIntUserData >     TIntUserDataList;
    typedef std::vector< TFloatUserData >   TFloatUserDataList;
    typedef std::vector< TVec3UserData >    TVec3UserDataList;
    
    class UserDataList
    {
    public:
        
        inline void pushUserData( size_t j, float* cur, size_t& k, int context );
        
        inline void listUserData( PrimitiveCache* pc, bool isSpline );
        
        inline void registerUserData( size_t& io_perPrim, size_t& io_perPoint, XGenMR::UserDataFormat& fmt, size_t& k, int* interpolateComponent );
        
        inline void registerUserData( size_t& io_perPrim, size_t& io_perPoint, XGenMR::UserDataFormat& fmt );
    private:
        TIntUserDataList vecInt;
        TFloatUserDataList vecFloat;
        TVec3UserDataList vecVec3;
    };
    
    class UserDataList; 
    class Procedural; 
    class BaseProcedural : public ProceduralCallbacks
    {
    public:
        BaseProcedural( const BaseProcedural* parentProc );
        virtual ~BaseProcedural();
        virtual bool get( EBoolAttribute ) const;
        virtual const char* get( EStringAttribute ) const;
        virtual float get( EFloatAttribute ) const;
        virtual const float* get( EFloatArrayAttribute ) const;
        virtual unsigned int getSize( EFloatArrayAttribute ) const;
        virtual const char* getOverride( const char* in_name ) const;
        virtual bool getArchiveBoundingBox( const char* in_filename, bbox& out_bbox ) const;
        virtual void getTransform( float in_time, mat44& out_mat ) const;
    private:
        BaseProcedural( );
        static void convertMatrix( const miMatrix in_mat, mat44& out_mat );
    protected:
        
        
        class Param
        {
        public:
            ~Param()
            {
                if( isString() )
                {
                    std::string* p = (std::string*)m_p;
                    delete p;
                }
                else if( isFloat() )
                {
                    float* p = (float*)m_p;
                    delete p;
                }
                else if( isFloatArray() )
                {
                    std::vector<float>* p = (std::vector<float>*)m_p;
                    delete p;
                }
                else if( isMatrixArray() )
                {
                    std::vector<miMatrixStruct>* p = (std::vector<miMatrixStruct>*)m_p;
                    delete p;
                }
                m_p = NULL;
            }
            Param( const std::string& in_str )
            {
                m_t = eString;
                std::string* s = new std::string;
                *s = in_str;
                m_p = (void*)s;
            }
            bool isString() { return m_t==eString; }
            std::string* asString()
            {
                return m_t==eString ? (std::string*)m_p : NULL;
            }
            Param( float in_f )
            {
                m_t = eFloat;
                float* f = new float;
                *f = in_f;
                m_p = (void*)f;
            }
            bool isFloat() { return m_t==eFloat; }
            float* asFloat()
            {
                return m_t==eFloat ? (float*)m_p : NULL;
            }
            Param( const std::vector<float>& in_f )
            {
                m_t = eFloatArray;
                std::vector<float>* f = new std::vector<float>;
                *f = in_f;
                m_p = (void*)f;
            }
            bool isFloatArray() { return m_t==eFloatArray; }
            std::vector<float>* asFloatArray()
            {
                return m_t==eFloatArray ? (std::vector<float>*)m_p : NULL;
            }
            Param( const std::vector<miMatrixStruct>& in_m )
            {
                m_t = eMatrixArray;
                std::vector<miMatrixStruct>* m = new std::vector<miMatrixStruct>;
                *m = in_m;
                m_p = (void*)m;
            }
            bool isMatrixArray() { return m_t==eMatrixArray; }
            std::vector<miMatrixStruct>* asMatrixArray()
            {
                return m_t==eMatrixArray ? (std::vector<miMatrixStruct>*)m_p : NULL;
            }
        private:
            enum ParamType
            {
                eString, eFloat, eFloatArray, eMatrixArray
            };
            ParamType m_t;
            void* m_p;
        };
        typedef std::map<std::string,Param*> ParamMap;
        ParamMap m_user, m_overrides;
        bool getString( const ParamMap& in_params, const char* in_name, const char*& out_value, bool in_user=false ) const;
        bool getFloat( const ParamMap& in_params, const char* in_name, float& out_value, bool in_user=false  ) const;
        unsigned int getArraySize( const ParamMap& in_params, const char* in_name, int in_eType, bool in_user=false  ) const;
        bool getFloatArray( const ParamMap& in_params, const char* in_name, const float*& out_value, bool in_user=false  ) const;
        bool getMatrixArray( const ParamMap& in_params, const char* in_name, const miMatrix*& out_value, bool in_user=false  ) const;
    };
    
    
    
    class CountHairDataProcedural : public BaseProcedural
    {
    public:
        CountHairDataProcedural( const Procedural& parentProc );
        virtual ~CountHairDataProcedural() {}
        void reset() {
            m_numPrims = 0;
            m_numPoints = 0;
        }
        
        static bool initFaceRenderer(CountHairDataProcedural* dProc, 
            PatchRenderer* patch, unsigned int f);
        
        virtual void flush(  const char* in_geom, PrimitiveCache* in_cache );
        virtual void log( const char* in_str ){}
        void render() { m_face->render();}
        inline unsigned int getNumPrims() const { return m_numPrims;}
        inline unsigned int getNumPoints() const { return m_numPoints;}
    private:
        CountHairDataProcedural();
        CountHairDataProcedural( const CountHairDataProcedural& );
        std::auto_ptr<FaceRenderer> m_face;
        
        miInteger m_approx_degree;
        miInteger m_approx_mode;
        miInteger m_approx_parametric_subdivisions;
        miScalar  m_approx_fine_sub_pixel_size;
        unsigned int m_numPrims;
        unsigned int m_numPoints;
    };
       
    
    
    class InitHairDataProcedural : public BaseProcedural
    {
    public:
        InitHairDataProcedural( const Procedural& parentProc, UserDataList* pUserData, miHair_list* pHair);
        virtual ~InitHairDataProcedural() {}
        
        static bool initFaceRenderer(InitHairDataProcedural* dProc, 
            PatchRenderer* patch, unsigned int f);
        
        virtual void flush(  const char* in_geom, PrimitiveCache* in_cache );
        virtual void log( const char* in_str ){}
        void render() { m_face->render();}
        inline bool isValid() { return m_numInterpolateComponents != -1;}
        inline miGeoIndex getNumScalarsPerPoint() const { return m_numScalarsPerPoint;}
        inline miGeoIndex getNumScalarsPerPrim() const { return m_numScalarsPerPrim;}
        inline miGeoIndex getNumInterpolateComponents() const { return m_numInterpolateComponents;} 
        inline const std::string& getUserDataStrFormat() const { return m_userDataStrFormat;}
    private:
        InitHairDataProcedural();
        InitHairDataProcedural( const InitHairDataProcedural& );
        std::auto_ptr<FaceRenderer> m_face;
        
        miInteger m_approx_degree;
        miInteger m_approx_mode;
        miInteger m_approx_parametric_subdivisions;
        miScalar  m_approx_fine_sub_pixel_size;
        
        UserDataList* m_pUserData;
        std::string m_userDataStrFormat;
        miHair_list* m_pHair;
        
        miGeoIndex m_numScalarsPerPoint;
        miGeoIndex m_numScalarsPerPrim;
        miGeoIndex m_numInterpolateComponents;
    };
    class FlushHairDataProcedural : public BaseProcedural
    {
    public:
        FlushHairDataProcedural(
            const Procedural& parentProc,
            miGeoIndex* pHairIndices,
            miScalar* pHairScalars,
            UserDataList* pUserData,
            miGeoIndex numScalarsPerPoint,
            miGeoIndex numScalarsPerPrim,
            miGeoIndex numInterpolateComponents,
            miGeoIndex numScalarsTotal,
            miGeoIndex numIndicesTotal);
        virtual ~FlushHairDataProcedural() {}
        
        static bool initFaceRenderer(FlushHairDataProcedural* dProc, 
            PatchRenderer* patch, unsigned int f);
        
        virtual void flush(  const char* in_geom, PrimitiveCache* in_cache );
        virtual void log( const char* in_str ){}
        void render() { m_face->render();}
    private:
        FlushHairDataProcedural();
        FlushHairDataProcedural( const FlushHairDataProcedural& );
        std::auto_ptr<FaceRenderer> m_face;
        
        
        miInteger m_approx_degree;
        miInteger m_approx_mode;
        miInteger m_approx_parametric_subdivisions;
        miScalar  m_approx_fine_sub_pixel_size;
        miScalar  m_motion_blur_multiplier;
        
        
        miGeoIndex* m_pHairIndices;
        miScalar* m_pHairScalars;
        UserDataList* m_pUserData;
        
        miGeoIndex m_numScalarsPerPoint;
        miGeoIndex m_numScalarsPerPrim;
        miGeoIndex m_numInterpolateComponents;
        
        miGeoIndex m_hairScalarsOffset;
        miGeoIndex m_hairIndicesOffset;
        miGeoIndex m_numScalarsTotal;
        miGeoIndex m_numIndicesTotal;
    };
    class FlushSphereProcedural : public BaseProcedural
    {
    public:
        FlushSphereProcedural(
            const Procedural& parentProc, 
            miTag sphereTag);
        virtual ~FlushSphereProcedural() {}
        
        static bool initFaceRenderer(FlushSphereProcedural* dProc, 
            PatchRenderer* patch, unsigned int f);
        miTag getResultTag() { return m_result;}
        
        virtual void flush(  const char* in_geom, PrimitiveCache* in_cache );
        virtual void log( const char* in_str ){}
        void render() { m_face->render();}
    private:
        FlushSphereProcedural();
        FlushSphereProcedural( const FlushSphereProcedural& );
        std::auto_ptr<FaceRenderer> m_face;
        std::string m_parentName; 
        miTag m_sphere; 
        miTag m_result;
        std::vector<miTag> m_tags;
    };
    class Procedural : public BaseProcedural
    {
    public:
        friend class ProceduralWrapper;
        friend class FlushHairDataProcedural;
        friend class InitHairDataProcedural;
        friend class CountHairDataProcedural;
        struct Params
        {
            miString data;      
            
            
            
            miString user;      
            miString overrides; 
            miScalar frame;
            miString patches;   
            
            miBoolean   echo;                   
            miString    echo_filename;          
            miBoolean   echo_ascii;             
            miInteger   echo_explode_objects;   
            miBoolean   echo_verbatim_textures; 
            miInteger   echo_dont;              
            miInteger   echo_dont_recurse;      
            
            miInteger approx_degree;
            miInteger approx_mode;
            miInteger approx_parametric_subdivisions;
            miScalar  approx_fine_sub_pixel_size;
            
            miBoolean motion_blur;
            miInteger motion_blur_mode;
            miInteger motion_blur_steps;
            miScalar  motion_blur_factor;
            miScalar  motion_blur_multiplier;
            
            miScalar max_displace;
            miScalar m_hair_object_size;        
            
            miInteger m_sphere_subdiv_u;
            miInteger m_sphere_subdiv_v;
        };
        Procedural();
        virtual ~Procedural();
        
        void init(miState* state, Params* paras, miBoolean *inst_init_req );
        void exit( miState* state, Params* paras );
        miBoolean execute( miTag* result, miState* state, Params* paras );
        
        void echo( miTag* result, miState* state, Params* paras );
        
        virtual void flush(  const char* in_geom, PrimitiveCache* in_cache );
        virtual void log( const char* in_str ){}
        
        bool render( miTag* result, const miState* state );
        
        
        void setPlaceholderObjectTag(const miTag tag) { m_tagPlaceholderObject = tag;}
        miTag getUserDataTag() { return m_tagUserData;}
        const std::string& getParentName() const { return m_parentName;}
    private:
        
        bool nextFace( bbox& b, unsigned int& f );
        bool initPatchRenderer( const char* in_params );
        bool initFaceRenderer( Procedural* pProc, unsigned int f );
        
        bool initPatchProcedural( Procedural* pParent, const std::string& strPatch );
        
        
        bool renderHairObject( miTag* result, const miState* state );
        
        miHair_list* beginHairObject( );
        void endHairObject( unsigned int numScalarsTotal, const std::string& strFormat );
        bool renderSphereAssembly();
        void flushCards( const char *geomName, PrimitiveCache* pc );
        void flushArchives( const char *geomName, PrimitiveCache* pc );
        void syncArchives( const char *geomName, PrimitiveCache* pc );
        
        const char* getUniqueName( char* buf, const char* basename );
        miTag m_node;
        typedef std::vector<Procedural*> TProcList;
        TProcList m_patches;
        PatchRenderer* m_patch;
        std::string m_patchName;
        FaceRenderer* m_face;
        std::string m_data;
        bool getString( const ParamMap& in_params, const char* in_name, const char*& out_value, bool in_user=false ) const;
        bool getFloat( const ParamMap& in_params, const char* in_name, float& out_value, bool in_user=false  ) const;
        unsigned int getArraySize( const ParamMap& in_params, const char* in_name, int in_eType, bool in_user=false  ) const;
        bool getFloatArray( const ParamMap& in_params, const char* in_name, const float*& out_value, bool in_user=false  ) const;
        bool getMatrixArray( const ParamMap& in_params, const char* in_name, const miMatrix*& out_value, bool in_user=false  ) const;
        miTag makeArchiveInstanceGroup( PrimitiveCache* pc, const std::string& instanceName, const std::string& instanceGroupName, const std::string& filename, const std::string& select, const std::string& material, miScalar frame, miInteger assembly );
        const miState* m_state;
        miTag m_result;
        miTag m_dummy;
        miTag m_tagUserData;
        std::string m_parentName;  
        std::string m_parentNameNoFace;
        std::string m_primType;
        std::vector<miTag> m_tags;
        std::vector<miTag> m_tagsHiddenGroup;
        
        miTag m_tagPlaceholderObject;
        miGeoIndex m_numHairPoints;
        miGeoIndex m_numHairPrims;
        unsigned int m_faceBegin;
        unsigned int m_faceEnd;
        std::map<std::string,std::string>* m_archives;
        bool m_bSyncArchives;
        bool m_bPerFaceAssemblies;
        bool m_bEcho;
        
        miInteger m_approx_degree;
        miInteger m_approx_mode;
        miInteger m_approx_parametric_subdivisions;
        miScalar  m_approx_fine_sub_pixel_size;
        bool m_motion_blur;
        miInteger m_motion_blur_mode;
        miInteger m_motion_blur_steps;
        miScalar  m_motion_blur_factor;
        miScalar  m_motion_blur_multiplier;
        miScalar  m_max_displace;
        miInteger m_sphere_subdiv_u;
        miInteger m_sphere_subdiv_v;
    };
};
#endif