#ifndef _cgfxShaderNode_h_
#define _cgfxShaderNode_h_
// Copyright (C) 2002-2003 NVIDIA
// File: cgfxShaderNode.h
// Dependency Graph Node: cgfxShaderNode
// Author: Jim Atkinson
// Changes:
//  10/2003  Kurt Harriman - +1-415-893-1023
//           - Multiple UV sets; user-specified texcoord assignment;
//             error handling.
//  12/2003  Kurt Harriman - +1-415-893-1023
//           - attrDefList() no longer calls fAttrDefList->addRef()
//           - Added members: fTechniqueList, getTechniqueList()
//           - Deleted members: fCallbackSet, callbackSet(),
//             setCallback(), technique()
//           - Deleted class cgfxShaderNode::InternalError.
//           - Made protected instead of public: setShaderFxFile,
//             setTechnique, setTexCoordSource
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
#include "cgfxShaderCommon.h"

#include <maya/MFnNumericAttribute.h>
#include <maya/MNodeMessage.h>
#include <maya/MObjectArray.h>
#include <maya/MCallbackIdArray.h>
#include <maya/MPxHwShaderNode.h>
#include <maya/MPxNode.h>
#include <maya/MStateManager.h>
#include <maya/MStringArray.h>
#include <maya/MTypeId.h>

// Viewport 2.0 includes
#include <maya/MPxShaderOverride.h>

#include "cgfxRCPtr.h"
#include "cgfxEffectDef.h"
#include "cgfxAttrDef.h"

#include <set>
#include <map>

class cgfxProfile;
class cgfxPassStateSetter;

// Largest possible number of texture units (GL_MAX_TEXTURE_UNITS) for any
// OpenGL implementation, according to the OpenGL 1.2 multitexture spec.

class cgfxShaderNode : public MPxHwShaderNode
        friend class cgfxAttrDef; // For setTexturesByName().
        friend class cgfxShaderOverride;


        virtual void        postConstructor();
        virtual                         ~cgfxShaderNode();

        // Typically, hardware shaders to not need to compute any outputs.
        virtual MStatus compute( const MPlug& plug, MDataBlock& data );

        // Create the node ...
        static  void*           creator();

        // ... and initialize it.
        static  MStatus         initialize();
        static  void        initializeNodeAttrs();

        // We want to track the changes to the shader attribute because
        // it causes us a large amount of work every time it is modified
        // and we want to do that work as rarely as possible.
        virtual void copyInternalData( MPxNode* pSrc );
        virtual bool getInternalValueInContext( const MPlug&,
    virtual bool setInternalValueInContext( const MPlug&,
                                                                                          const MDataHandle&,

        // Tell Maya that Cg effects can be batched
        virtual bool    supportsBatching() const;

        // Tell Maya we require inverted texture coordinates
        virtual bool    invertTexCoords() const;

        // Indicate pass transparency options
        virtual bool    hasTransparency();
        virtual unsigned int    transparencyOptions();

        // Set up the graphics pipeline for drawing with the shader
        virtual MStatus glBind(const MDagPath& shapePath);
        void            bindAttrValues();
        void            bindViewAttrValues(const MDagPath& shapePath);

        // Render a swatch
        virtual MStatus renderSwatchImage( MImage & image );

        // Restore the graphics pipeline back to its original state
        virtual MStatus glUnbind(const MDagPath& shapePath);

        // Put the pixels on the screen
        virtual MStatus         glGeometry( const MDagPath& shapePath,
                int prim,
                unsigned int writable,
                int indexCount,
                const unsigned int * indexArray,
                int vertexCount,
                const int * vertexIDs,
                const float * vertexArray,
                int normalCount,
                const float ** normalArrays,
                int colorCount,
                const float ** colorArrays,
                int texCoordCount,
                const float ** texCoordArrays);

        // Return the desired number of texture coordinates per vertex
        virtual int     getTexCoordSetNames( MStringArray& names );

        // Return the desired colours per vertex
        virtual int     getColorSetNames( MStringArray& names );
        virtual int             colorsPerVertex();

        // Return the desired number of normals per vertex
        virtual int             normalsPerVertex();

        virtual MStatus getAvailableImages( const MString& uvSetName,
                                                                                MStringArray& imageNames);

        virtual MStatus renderImage( const MString& imageName,
                                                                floatRegion region,
                                                                const MPxHwShaderNode::RenderParameters& parameters,
                                                                int& imageWidth,
                                                                int& imageHeight);

        // Data accessor for the attrDefList
        const cgfxRCPtr<cgfxAttrDefList>& attrDefList() const { return fAttrDefList; };

        void setAttrDefList(const cgfxRCPtr<cgfxAttrDefList>& list);

        // Data accessor for the .fx file name (the value of the shader
        // attribute).
        MString shaderFxFile() const { return fShaderFxFile; };
        bool shaderFxFileChanged() const { return fShaderFxFileChanged; }
        void setShaderFxFileChanged(bool val) { fShaderFxFileChanged = val; if (val) ++fGeomReqDataVersionId; }

        // Data accessor for the name of the chosen technique.
        MString         getTechnique() const { return fTechnique; };
        const MStringArray& getTechniqueList() const { return fTechniqueList; }

        // Data accessor for the name of the chosen profile.
        MString         getProfile() const { return fProfileName; };

        // Data accessor the attribute list string (the value of the
        // attributeList attribute).
        void getAttributeList(MStringArray& attrList) const;
        void setAttributeList(const MStringArray& attrList);

        // Set the current per-vertex attributes the shader needs (replacing any existing set)
        void                            setVertexAttributes( cgfxRCPtr<cgfxVertexAttribute> attributeList);

        // Set the data set names that will be populating our vertex attributes
        void                            setVertexAttributeSource( const MStringArray& sources);

        // Analyse the per-vertex attributes to work out the minimum set of data we require
        void                            analyseVertexAttributes();

        // Data accessor for the texCoordSource attribute.
        const MStringArray& getTexCoordSource() const;

        // Data accessor for the colorSource attribute.
        const MStringArray& getColorSource() const;

        // Data accessor for list of empty UV sets.
        const MStringArray& getEmptyUVSets() const;
        const MObjectArray& getEmptyUVSetShapes() const;

        // Data accessor for whether to use nodes for textures
        inline bool getTexturesByName() const { return fTexturesByName; };

        // // Data accessor for the effect itself.
        // //
        const cgfxRCPtr<const cgfxEffect>& effect() { return fEffect; };
        void setEffect(const cgfxRCPtr<const cgfxEffect>& effect);

        MStatus shouldSave ( const MPlug & plug, bool & ret );

        // Get cgfxShader version string.
        static MString  getPluginVersion();


        // Try and create a missing effect (e.g. once a GL context is available)
        bool                    createEffect();

        // Set internal attributes.
        void            setShaderFxFile( const MString& fxFile )
                if (fxFile != fShaderFxFile)
                        // Mark when the shader has changed
                        fShaderFxFile = fxFile;
                        fShaderFxFileChanged = true;
        void            setTechnique( const MString& techn );
        void            setProfile( const MString& profileName );
        void            setProfile( const cgfxProfile* profile );
        void            setDataSources( const MStringArray* texCoordSources, const MStringArray* colorSources );
        void            updateDataSource( MStringArray& sources, MIntArray& typeList, MIntArray& indexList );
        void            setTexturesByName( bool texturesByName, bool updateAttributes = false);
    void            updateTechniqueList();

        // Error reporting
        void            reportInternalError( const char* function, size_t errcode);


        // The typeid is a unique 32bit indentifier that describes this node.
        // It is used to save and retrieve nodes of this type from the binary
        // file format.  If it is not unique, it will cause file IO problems.
        static  MTypeId sId;

        // There needs to be a MObject handle declared for each attribute that
        // the node will have.  These handles are needed for getting and setting
        // the values later.
        // Input shader attribute
        static  MObject sShader;
        static  MObject sTechnique;
        static  MObject sProfile;

        // Input list of attributes that map to CgFX uniform parameters
        static  MObject sAttributeList;

        // Input list of attributes that map to CgFX varying parameters
        static  MObject sVertexAttributeList;

        // Input list of where to get vertex attributes
        static  MObject sVertexAttributeSource;

        // Input list of where to get texcoords
        static  MObject sTexCoordSource;

        // Input list of where to get colors
        static  MObject sColorSource;

        // Should we use string names or file texture nodes for textures?
        static  MObject sTexturesByName;

        // Cg Context
        static  CGcontext       sCgContext;

        // Cg error callback
        static void                     cgErrorCallBack();

        // Cg error handler
        static void                     cgErrorHandler(CGcontext cgContext, CGerror cgError, void* userData);


        // Description of the effect and it's varying parameters
        cgfxRCPtr<const cgfxEffect> fEffect;

        // The (merged) set of varying parameters the current technique requires
        // and the mapping onto Maya geometry data sets
        cgfxRCPtr<cgfxVertexAttribute>  fVertexAttributes;

        // This is a mapping of names to cgfxAttrDef pointers.  Each cgfxAttrDef
        // defines one dynamic attribute
    cgfxRCPtr<cgfxAttrDefList> fAttrDefList;

        // Values of internal attributes
        MString                         fShaderFxFile;
        bool                            fShaderFxFileChanged;
        MString                         fTechnique;
    const cgfxTechnique*    fCurrentTechnique;
        MString                 fProfileName;
        MStringArray            fAttributeListArray;
        MStringArray            fVertexAttributeListArray;
        MStringArray            fVertexAttributeSource;
        bool                            fTexturesByName;

        // Backward compatibility: these are the old versions of fVertexAttributeSource
        MStringArray    fTexCoordSource;
        MStringArray    fColorSource;

        // The list of maya data we need
        MStringArray    fUVSets;
        MStringArray    fColorSets;
        int             fNormalsPerVertex;

        // These values are derived from the fTexCoordSource value
        // For each texture coord / colour unit, we describe what kind of
        // data should go in there (i.e. should this data be populated with
        // a uv set, a colour set, tangent, normal, etc) AND which set of
        // data to use (i.e. put uv set 0 in texcoord1, and colour set 2 in
        // texcoord 5)
        MIntArray       fTexCoordType;
        MIntArray               fTexCoordIndex;
        MIntArray       fColorType;
        MIntArray               fColorIndex;
        MStringArray    fDataSetNames;

        // Cached info derived from the current cgfxEffect.
        MStringArray    fTechniqueList;

    // Cache of bound data streams. Used only by the non-VP2.0
    // implementation.
    cgfxStructureCache fBoundDataCache;

    // Pass state setter used in the VP2.0 implementation.
    cgfxPassStateSetter* fPassStateSetters;

        // Rendering state (only valid between glBind and glUnbind)
        GLboolean               fDepthEnableState;
        GLint                   fDepthFunc;
        GLint                   fBlendSourceFactor;
        GLint                   fBlendDestFactor;

        // Error handling
        bool            fConstructed;      // true => ok to call MPxNode member functions
        short           fErrorCount;
        short           fErrorLimit;

        // Maya event callbacks
        MCallbackIdArray fCallbackIds;

    // Version Id for data that influences the geometry requirements
    // computed by the cgfxShaderOverride node. It is incremented each
    // time, one of the associated data changes.
    int             fGeomReqDataVersionId;

        typedef std::set<cgfxShaderNode*> NodeList;
        static void getNodesUsingEffect(const cgfxRCPtr<const cgfxEffect>& effect, NodeList &nodes);

        // Keep track of all the effect-node association
        typedef std::map<const cgfxEffect*, NodeList> Effect2NodesMap;
        static Effect2NodesMap sEffect2NodesMap;

        static void addAssociation(cgfxShaderNode* node, const cgfxRCPtr<const cgfxEffect>& effect);
        static void removeAssociation(cgfxShaderNode* node, const cgfxRCPtr<const cgfxEffect>& effect);

        // Watch for attribute removals to avoid crashes
        static void attributeAddedOrRemovedCB(MNodeMessage::AttributeMessage msg, MPlug& plug, void* clientData);

// ===================================================================================
// viewport 2.0 implementation
// ===================================================================================

namespace MHWRender
        class MDrawContext;

// Override for the cgfxShaderNode
class cgfxShaderOverride : public MHWRender::MPxShaderOverride
        static MHWRender::MPxShaderOverride* Creator(const MObject& obj);

        virtual ~cgfxShaderOverride();

        // Initialize phase
        virtual MString initialize(MObject shader);

        // Update phase
        virtual void updateDG(MObject object);
        virtual void updateDevice();
        virtual void endUpdate();

        // Draw phase
    virtual void activateKey(MHWRender::MDrawContext& context);
        virtual bool draw(MHWRender::MDrawContext& context,
                      const MHWRender::MRenderItemList& renderItemList) const;
    virtual void terminateKey(MHWRender::MDrawContext& context);

        // Override properties
        virtual MHWRender::DrawAPI supportedDrawAPIs() const;
        virtual bool isTransparent();
        virtual bool overridesDrawState();

        virtual bool rebuildAlways()
                return !fShaderNode ||
            fShaderNode->fGeomReqDataVersionId != fGeomReqDataVersionId;

        static const MString drawDbClassification;
        static const MString drawRegistrantId;

        cgfxShaderOverride(const MObject& obj);

        // bind uniform attributes
        bool bindAttrValues() const;

        // bind varying attributes
        void bindViewAttrValues(const MHWRender::MDrawContext& context) const;

    cgfxShaderNode *fShaderNode;
    int fGeomReqDataVersionId;
    mutable bool fNeedPassSetterInit;

    const MHWRender::MBlendState* fOldBlendState;
    const MHWRender::MDepthStencilState* fOldDepthStencilState;
    const MHWRender::MRasterizerState* fOldRasterizerState;

    static cgfxShaderNode* sActiveShaderNode;
    static cgfxShaderNode* sLastDrawShaderNode;

#endif /* _cgfxShaderNode_h_ */