// Miscellaneous Utilities used for interaction with Maya

#pragma once

// finds the skin cluster by a shape
// returns kNullObj if no skin cluster was found
extern MObject findSkinCluster (const MObject& objShape, MItDependencyGraph::Direction nDirection = MItDependencyGraph::kUpstream);

// MDagPath version of the same function
inline MObject findSkinCluster (const MDagPath& pathShape, MItDependencyGraph::Direction nDirection = MItDependencyGraph::kUpstream)
{	return findSkinCluster (pathShape.node(), nDirection); }

template <typename T>
T getAttrValue (const MObject& objNode, const char* szAttrName, T tDefault = T(), MStatus* status = NULL)
{
	MStatus statusInternal;
	if (!status)
		status = &statusInternal;

	MFnDependencyNode fnNode (objNode, status);
	if (!*status)
		return tDefault;
	MPlug plug = fnNode.findPlug(szAttrName, status);
	if (!*status)
		return tDefault;
	T strResult;
	*status = plug.getValue (strResult);
	return strResult;
}


// escapes the given string so that, when passed to MEL, it is un-escaped and converted into the original string
// E.g.: C:\my-file.cgf is converted into C:\\my-file.cgf
MString escapeMString (const MString& strOriginal);


// returns the value of the given string plug; if there's no plug, returns an empty string and an error
inline MString getAttrValueString (const MObject& objNode, const char* szAttrName, MStatus* status = NULL)
{
	return getAttrValue<MString>(objNode, szAttrName, "", status);
}

// returns the value of the given bool plug; if there's no plug or an error happened, returns the default value and the error in status
inline bool getAttrValueBool (const MObject& objNode, const char* szAttrName, bool bDefaultResult, MStatus* status = NULL)
{
	return getAttrValue<bool>(objNode, szAttrName, bDefaultResult, status);
}

// returns the value of the given int plug; if there's no plug or an error happened, returns the default value and the error in status
inline int getAttrValueInt (const MObject& objNode, const char* szAttrName, MStatus* status = NULL)
{
	return getAttrValue<int>(objNode, szAttrName, 0, status);
}

// reduces the given path to transform path
extern MDagPath reduceToTransform (const MDagPath& pathShape);

// Binary comparision predicate for sorting MDagPath arrays and constructing maps
struct MDagPathOrder
{	bool operator () (const MDagPath& left, const MDagPath& right) const; };

// set of DAG nodes, sorted by full path
typedef std::set<MDagPath, MDagPathOrder> MDagPathSet;

// makes the text list out of the given set of objects
extern std::string toString (const MDagPathSet& setPaths);

// for the given joint, returns the root of the skeleton it belongs to
MDagPath getHierarchyRoot (const MDagPath& pathJoint);

// finds the lowest common parent in the DAG for all the given paths
MDagPath getCommonParent (const MDagPathSet& setPathResult);


// Finds the object connected to the given object via the given attribute
// returns kNullObj if attribute not found or error
extern MObject findConnected (const MObject& obj, const MString& strAttribute, bool bIncomingConnection);

// Finds out if the given node has a flipped transformation (negative parity)
// return true if the node parity is positive (right-handed coordinate system) and false otherwise
extern bool getNodeParity (const MDagPath& pathNode);

// returns the verbose string corresponding to the given rotation order constant
extern const char* getRotOrdStr (MTransformationMatrix::RotationOrder nRotOrd);

// finds the influence objects (bones) influencing the given shape
// does not affect the given array if no bones were found;
// throws a MStatus in the case of a fatal error
extern void findShapeInfluences (const MObject& objShape, MDagPathArray& arrBones);

// This class is used for turning off auto-animation temporarily.
// Upon construction, an instance of this class will save the animation state of Maya and
// set auto-animation to off. After that, any transformations can be changed without the risk
// of introducing extra keys or otherwise influencing the scene.
// upon destruction, it restores the saved state.
// This may also involve other shadowed operations.
class CMayaAutoAnimTempState
{
public:
	CMayaAutoAnimTempState();
	~CMayaAutoAnimTempState();
protected:
	bool m_bAutoKeyMode;
};

// set of MObject's
class MObjectSet: protected MObjectArray
{
public:
	// inserts the given object into the set, returns the position in the set
	unsigned insert (const MObject& obj);
	// erases the object from the set, returns true if the object was found and erased
	bool erase (const MObject& obj);
	// returns the size of the set
	unsigned size()const;
	// returns true if the size() == 0
	bool empty()const ;
	// returns the i-th element of the set
	MObject& operator [] (unsigned i);
	// returns the i-th element of the set
	const MObject& operator [] (unsigned i)const;
};
