//
// Copyright (C) 2002-2003 NVIDIA 
// 
// File: cgfxAttrDef.h
//
// Utilities for dealing with CgFX shaders
//
// Author: Jim Atkinson
//
// Changes:
//  12/2003  Kurt Harriman - www.octopusgraphics.com +1-415-893-1023
//           - Added cgfxAttrDef members:  fNumericSoftMin, fNumericSoftMax,
//             fSemantic, fTweaked, fInitOnUndo, typeName(),
//             compoundAttrSuffixes(), attrFromNode(), initializeAttributes(),
//             isInitialValueEqual(), setInitialValue(), purgeMObjectCache(),
//             validateMObjectCache(), getExtraAttrSuffix()
//           - Deleted members: fMustInit, setDefaultValues()
//           - Changed members: fTextureId
//           - Added cgfxAttrDefList member: findInsensitive()
//           - Use MDGModifier instead of MDagModifier
//           - Minimizing unnecessary #includes so other Maya tools can
//             borrow this header without pulling in extra dependencies.
//
//-
// ==========================================================================
// 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.
// ==========================================================================
//+
#ifndef _cgfxAttrDef_h_
#define _cgfxAttrDef_h_

// This class holds the definition of a single attribute
// as it has been extracted from the CGeffect interface.
// All the data has been stored in native Maya format for
// simplicity of use.
//

#include <maya/MObject.h>
#include <maya/MString.h>
#include <maya/MMatrix.h>
#include <maya/MImage.h>
#include <maya/MMessage.h>
#include <maya/MDistance.h>

#include "cgfxRCPtr.h"

#include <Cg/cg.h>
#include <Cg/cgGL.h>

class  cgfxAttrDefList;                // below
class  cgfxEffect;                     // in "cgfxEffectDef.h"
class  cgfxShaderNode;                 // in "cgfxShaderNode.h"
class  cgfxTextureCacheEntry;
class  MDGModifier;                    // in <maya/MDGModifier.h>
class  MFnDependencyNode;              // in <maya/MFnDependencyNode.h>
#define kNullCallback 0

//--------------------------------------------------------------------//
//                            cgfxAttrDef                             //
//--------------------------------------------------------------------//
// Hold the definition of an attribute.

class cgfxAttrDef
{
public:
	enum cgfxAttrType
	{
		// Unknown.
		//
		kAttrTypeUnknown,

		// Boolean value.  Create with MFnNumericAttribute; set default
		// value
		//
		kAttrTypeBool,

		// Integer value.  Create with MFnNumericAttribute; set min,
		// max, default value
		//
		kAttrTypeInt,

		// Float value.  Create with MFnNumericAttribute; set min, max,
		// default value
		//
		kAttrTypeFloat,

		// String value.  Create with MFnTypedAttribute; set default
		// value
		//
		kAttrTypeString,

		// 2, 3, and 4 element vectors. Create with MFnNumericAttribute;
		// children are *X, *Y, *Z, and (maybe) *W; set min, max,
		// default for each child.
		//
		kAttrTypeVector2,
		kAttrTypeVector3,
		kAttrTypeVector4,

		kAttrTypeObjectDir,		// Object coordinates
		kAttrTypeFirstDir = kAttrTypeObjectDir,
		kAttrTypeWorldDir,		// World coordinates
		kAttrTypeViewDir,		// Eye coordinates
		kAttrTypeProjectionDir,	// Clip coordinates
		kAttrTypeScreenDir,		// Screen coordinates
		kAttrTypeLastDir = kAttrTypeScreenDir,

		kAttrTypeObjectPos,		// Object coordinates
		kAttrTypeFirstPos = kAttrTypeObjectPos,
		kAttrTypeWorldPos,		// World coordinates
		kAttrTypeViewPos,		// Eye coordinates
		kAttrTypeProjectionPos,	// Clip coordinates
		kAttrTypeScreenPos,		// Screen coordinates
		kAttrTypeLastPos = kAttrTypeScreenPos,

		// Color value.  Create with MFnCompoundAttribute; children are
		// *R, *G, *B, and (maybe) *A; set min, max, default for each
		// child.
		//
		kAttrTypeColor3,
		kAttrTypeColor4,

		// Matrix value.  Create with MFnMatrixAttribute; default is
		// identity.
		//
		kAttrTypeMatrix,
		kAttrTypeFirstMatrix = kAttrTypeMatrix,
		kAttrTypeWorldMatrix,
		kAttrTypeViewMatrix,
		kAttrTypeProjectionMatrix,
		kAttrTypeWorldViewMatrix,
		kAttrTypeWorldViewProjectionMatrix,
		kAttrTypeLastMatrix = kAttrTypeWorldViewProjectionMatrix,

		// Texture types.  Create as a color connected to a new file
		// texture node.
		//
		kAttrTypeColor1DTexture,
		kAttrTypeFirstTexture = kAttrTypeColor1DTexture,
		kAttrTypeColor2DTexture,
		kAttrTypeColor3DTexture,
		kAttrTypeColor2DRectTexture,
		kAttrTypeNormalTexture,
		kAttrTypeBumpTexture,
		kAttrTypeCubeTexture,
		kAttrTypeEnvTexture,
		kAttrTypeNormalizationTexture,
		kAttrTypeLastTexture = kAttrTypeNormalizationTexture,

#ifdef _WIN32
		// Time
		kAttrTypeTime,
#endif
		// Other value.  This is for attributes that have odd type or
		// dimensionality.  Create them as multi, multi, multi
		// attributes as needed; default is zero.
		//
		kAttrTypeOther,

		// Last value.
		_kAttrTypeLast
	};

	// Static methods to return useful info about a cgfxAttrType.
	static const char*  typeName( cgfxAttrType eAttrType );
	static const char** compoundAttrSuffixes( cgfxAttrType eAttrType );

	// These are hints as to what might be connected to a direction or
	// position input.
	//
	enum cgfxVectorHint
	{
		kVectorHintNone,
		kVectorHintDirLight,
		kVectorHintPointLight,
		kVectorHintSpotLight,
		kVectorHintEye,

		_kVectorHintLast
	};

	//----------------------------------------------------------------//

	MString			fName;
	cgfxAttrType	fType;
	int				fSize;			// The number of elements, not bytes.
	MObject			fAttr;			// The attribute itself
	cgfxVectorHint	fHint;

	// Vector4 or Color4 types use an extra Maya attribute to hold W or Alpha.
	MObject			fAttr2;			// An extra attribute if necessary.

	double*			fNumericMin;	// we use doubles even for int data.
	double*			fNumericMax;
	double*         fNumericSoftMin;
	double*         fNumericSoftMax;
	double*         fNumericDef;    // Numeric initial value
	MDistance::Unit	fUnits;

	MString         fStringDef;     // String initial value

	MString         fDescription;   // Description (if supplied)
	MString         fSemantic;      // Semantic (if supplied)

	CGparameter     fParameterHandle;

    cgfxRCPtr<cgfxTextureCacheEntry> fTexture;
	MCallbackId		fTextureMonitor;
	MString			fTextureUVLink;	// A string value for texture UV link.

	bool			fInvertMatrix;	// Matrix elements should be inverted.
	bool			fTransposeMatrix;//Matrix elements should be transposed.

	bool            fTweaked;       // true => user has changed attr value
	bool            fInitOnUndo;    // true => set attr to initial value when
	//   changing back to this effect on undo

private:
	bool			fIsConvertedToInternal;
	// the symbol to mark if the unit is converted to internal one
	const static char fSymbol;

	//----------------------------------------------------------------//

public:
	// Constructor
	cgfxAttrDef(CGparameter cgParameter);
    cgfxAttrDef(
        const MString&          sAttrName,
        const cgfxAttrType      eAttrType,
        const MString&          sDescription,
        const MString&          sSemantic,
        MObject                 obNode,
        MObject                 obAttr
    );

	// Destructor
	~cgfxAttrDef();

private:
	// Prohibited and not implemented.
	cgfxAttrDef(const cgfxAttrDef&);
	const cgfxAttrDef& operator=(const cgfxAttrDef&);

public:
	// Release any associated resources
	void			release();
	void			releaseTexture();
	void			releaseCallback();

	// Return a string representation of fType.
	MString         typeName() const { return typeName( fType ); }

	static cgfxRCPtr<cgfxAttrDefList> attrsFromNode(MObject& node);
    
	static void     updateNode(
        const cgfxRCPtr<const cgfxEffect>& effect,
		cgfxShaderNode*                     pNode, 
		MDGModifier*                        dgMod,
        cgfxRCPtr<cgfxAttrDefList>&         attrDefList,
		MStringArray&                       attributeList);
	static void buildAttrDefList(MObject& node);
	static void     initializeAttributes(
        MObject&                            node, 
        const cgfxRCPtr<cgfxAttrDefList>&   list,
		bool                                bUndoing,
		MDGModifier*	                    dgMod );
	static void     purgeMObjectCache(const cgfxRCPtr<cgfxAttrDefList>& list);
	static void     validateMObjectCache(const MObject&   obCgfxShader, 
                                         const cgfxRCPtr<cgfxAttrDefList>& list);
    
	static cgfxAttrDef* attrFromNode(
		const MFnDependencyNode& fnNode,
		const MString&           sAttrName,
		const cgfxAttrType       eAttrType,
		const MString&           sDescription,
		const MString&           sSemantic);

	// Return suffix for Color4/Vector4 extra attribute, or NULL.
	const char*     getExtraAttrSuffix() const;

	bool            createAttribute(const MObject& oNode, MDGModifier* mod, cgfxShaderNode*   pNode);
	bool            destroyAttribute(MObject& oNode, MDGModifier* mod);

protected:

	// setTextureType
	//
	// Description:
	//
	// Parameters:
	//      param - parameter
	//
	// Returns:
	//      None
	//
	void setTextureType(CGparameter param);

	void setSamplerType(CGparameter param);


	void setMatrixType(CGparameter param);

	// setVectorType
	//
	// Description:
	//  
	// Parameters:
	//      param - parameter
	//
	// Returns:
	//      None
	//
	void 

		setVectorType(CGparameter param);

	// Return true if initial value of 'this' is same as 'that'.
	bool            isInitialValueEqual( const cgfxAttrDef& that ) const; 

	// Copy initial value from given attribute.
	void            setInitialValue( const cgfxAttrDef& from );

    // Set attribute flag
	void setAttributeFlags();

public:
	// These routines all get the value of the attribute referenced by 
	// this cgfxAttrDef object.
	//
	void            getValue( MObject& oNode, bool& value ) const;
	void            getValue( MObject& oNode, int& value ) const;
	void            getValue( MObject& oNode, float& value ) const;
	void            getValue( MObject& oNode, MString& value ) const;
	void            getValue( MObject& oNode, float& v1, float& v2 ) const;
	void            getValue( MObject& oNode,
		float& v1, float& v2, float& v3 ) const;
	void            getValue( MObject& oNode,
		float& v1, float& v2, float& v3, float& v4 ) const;
	void            getValue( MObject& oNode, MMatrix& value ) const;
	void            getValue( MObject& oNode, MImage& value ) const;

	// This routine finds the DG input to this attribute (e.g. texture
	// node connections)
	//
	void            getSource( MObject& oNode, MPlug& src) const;


	// These routines all set the value of the attribute referenced by 
	// this cgfxAttrDef object.
	//
	void            setValue( MObject& oNode, bool value );
	void            setValue( MObject& oNode, int value );
	void            setValue( MObject& oNode, float value );
	void            setValue( MObject& oNode, const MString& value );
	void            setValue( MObject& oNode, float v1, float v2 );
	void            setValue( MObject& oNode, float v1, float v2, float v3 );
	void            setValue( MObject& oNode,
		float v1, float v2, float v3, float v4 );
	void            setValue( MObject& oNode, const MMatrix& v );
	void            setTexture( MObject& oNode, const MString& value, MDGModifier* dgMod);

	void			setUnitsToInternal(CGparameter& cgParameter);
};

#define VALID(ptr) (ptr == 0 || _CrtIsValidHeapPointer(ptr))


//--------------------------------------------------------------------//
//                          cgfxAttrDefList                           //
//--------------------------------------------------------------------//
// Hold a list of cgfxAttrDef* objects that can be searched by name

class cgfxAttrDefList
{
protected:
	class element;
	friend class element;

public:
	class iterator;
	friend class iterator;

protected:
	class element
	{
	public:
		element*		prev;
		element*		next;
		cgfxAttrDef*	data;

		element()
			: prev(0)
			, next(0)
			, data(0)
		{ /* nothing */ };

		~element()
		{
#ifdef _WIN32
			_ASSERTE(VALID(prev));
			_ASSERTE(VALID(next));
			_ASSERTE(VALID(data));
#endif
			if (prev)
			{
				prev->next = next;
			}
			if (next)
			{
				next->prev = prev;
			}
			delete data;
		};
	};

	element* head;
	element* tail;
	int refcount;

public:
	class iterator
	{
		friend class cgfxAttrDefList;

	protected:
		element*		item;

	public:
#ifdef _WIN32
		cgfxAttrDef* operator *() { _ASSERTE(VALID(item)); _ASSERTE(VALID(item->data)); return item->data; };
		iterator& operator ++ () { _ASSERTE(VALID(item)); _ASSERTE(VALID(item->next)); item = item->next; return *this; };
#else
		cgfxAttrDef* operator *() { return item->data; };
		iterator& operator ++  () { item = item->next; return *this; };
#endif

		iterator operator ++ (int)
		{
			iterator tmp = *this;
#ifdef _WIN32
			_ASSERTE(VALID(item));
			_ASSERTE(VALID(item->next)); 
#endif
			item = item->next;
			return tmp;
		};

		operator bool() { return (item != 0); };

		iterator(): item(0) { /* nothing */ };
		iterator(cgfxAttrDefList& list): item(list.head)
	   	{
#ifdef _WIN32
			   	_ASSERTE(VALID(item));
#endif
	   	};
		iterator(const cgfxRCPtr<cgfxAttrDefList>& list)
		{
#ifdef _WIN32
			_ASSERTE(VALID(list.operator->()));
			item = list.isNull() ? 0 : list->head;
			_ASSERTE(VALID(item));
#else
			item = list.isNull() ? 0 : list->head;
#endif
		};
		void reset() { item = 0; };

	protected:
		iterator(element* e): item(e) { /* nothing */ };
	};

	cgfxAttrDefList()
		:	head(0)
		,	tail(0)
		,	refcount(0)
	{ /* nothing */ };

private:

    friend class cgfxRCPtr<cgfxAttrDefList>;
    
	~cgfxAttrDefList()
	{
		clear();
	};

	void addRef()
	{
		++refcount;
	};

	void release();

    // Prohibited and not implemented.
	cgfxAttrDefList(const cgfxAttrDefList&);
	const cgfxAttrDefList& operator=(const cgfxAttrDefList&);

public:

    void releaseTextures();
    
    void clear()
	{
#ifdef _WIN32
		_ASSERTE(VALID(head));
#endif
		if (head)
		{
			element *tmp = new element;
			tmp->next = head;
			head->prev = tmp;

			while (tmp->next)
			{
				delete tmp->next;
			}
			delete tmp;
		}

		head = tail = 0;
	};

	bool empty() { return (head == 0); };

	iterator find(const MString& name)
	{
		iterator it(*this);

		while (it)
		{
			if ((*it)->fName == name)
			{
				break;
			}
			++it;
		}

		return it;
	};

	iterator        findInsensitive( const MString& name );

	void add(cgfxAttrDef* aDef)
	{
		element * e = new element;
		e->data = aDef;
		if (tail)
		{
			tail->next = e;
			e->prev = tail;
		}
		else
		{
			head = e;
		}
		tail = e;
	}

	iterator begin()
	{
		return iterator(*this);
	};

    void dump(const char* name);
};

#undef VALID
#endif /* _cgfxAttrDef_h_ */
