// Declaration of CMayaCryBoneUtil

#pragma once

#include "MayaUtils.h"

//////////////////////////////////////////////////////////////////////////
// class CMayaCryBoneUtil
// Used for conversion between Maya and CryEngine bone hierarchy formats.
// If you pass in geometry, finds the skin cluster and all influencing bones;
// then expands the influencing bones to the full hierarchy and linearizes it.
// The linearized hierarchy is used to serialize the bone hierarchy in Cry Engine
class CMayaCryBoneUtil
{
public:
	// initializes an emtpy bone hierarchy class
	CMayaCryBoneUtil();
	~CMayaCryBoneUtil();

	// adds the given shape's influencing nodes to the list of bones and returns
	// the number of influencing bones (if 0, then the shape is static)
	int addShapeInfluences(const MObject& objShape);

	// returns the number of bones in the hierarchy
	int numBones();

	// linearized bone/skeleton hierarchy entry: contains the bone reference
	// and the number of children stored in the hierarchy
	struct BoneEntry
	{
		// Dag path identifying the bone
		MDagPath pathBone;

		// number of children this bone has
		int numChildren;
		
		enum {kNoBone = -1};
		// index of the parent bone; kNoBone (-1) if no parent
		int nParentId; 

		BoneEntry (const MDagPath& _pathBone, int _numChildren, int _nParentId):
			pathBone (_pathBone),
			numChildren(_numChildren),
			nParentId(_nParentId)
		{
		}
	};
	// the linearized bone hierarchy: each bone is followed by the array
	// of linearized hierarchies of its children
	typedef std::vector<BoneEntry> BoneHierarchy;

	// returns the ith bone object
	const BoneEntry& getBone(int nBone);

	// returns the array of bones valid for the current state of the utility
	void getBones(BoneHierarchy& arrBones);

	// returns the index of the given bone in the linearized hierarchy, or -1 if not found
	int getBoneIndex (const MDagPath& pathBone);

	// fills in the index map for the given array of dag paths:
	// for i-th DagPath, it's index is put into the i-th element of the passed array
	void getBoneIndexMap (const MDagPathArray& arrBones, std::vector<int>& arrMap);

	// adds another bone to the array of root bones
	void addRootBone (const MDagPath& pathRootBone);

	// returns the number of actual roots (hierarchies) in the list of bones
	unsigned numRoots()const;

	// returns the ith root bone
	const MDagPath& getRoot (unsigned i)const;

	// puts all bones into the rest position
	// this does NOT save the positions
	void resetFromRestPosition ();

	// saves all bones' transform position
	void pushPosition();

	// restores all bones' position
	void popPosition();

	// cleans up the object
	void clear();

	// returns the comma-separated list of root bones
	MString getRootBoneDump ()const;
protected:
	// validates the linearized hierarchy (m_arrBones) - constructs it from the m_arrRootBones if necessary
	void validateBoneHierarchy();

	// flags the bone hierarchy (m_arrBones) invalid so that the next time the validate function is called, it's recalculated
	void invalidateBoneHierarchy();

	// constructs the bone hierarchy out from the array of root bones.
	// assumes that the root bones are not children of each other
	void constructBoneHierarchy();

	// adds the node and its subhierarchy to the array of bones and bone index map
	void addBoneHierarchy (MDagPath& pathBone, int nParentId);
protected:
	// the linearized bone hierarchy (full, gets rebuilt everytime the root array is changed)
	BoneHierarchy m_arrBones;

	// the map to quickly find the bone index by its path
	typedef std::map<MDagPath, int, MDagPathOrder> BoneIndexMap;
	BoneIndexMap m_mapBoneIndex;

	// the root bone hierarchy
	MDagPathArray m_arrRootBones;

	// the array of saved transforms
	typedef std::vector<MTransformationMatrix> BonePositions;
	typedef std::stack<BonePositions> BonePositionStack;
	BonePositionStack m_stkPositions;
};
