//Copyright © 2008 Autodesk, Inc.
//All Rights Reserved.
//
//The coded instructions, statements, computer programs, and/or related material (collectively "Data")
//in these files contain unpublished information proprietary to Autodesk, Inc., ("Autodesk") which is
//protected by Canada and United States of America federal copyright law and by international treaties.
//
//The Data may not be disclosed or distributed to third parties, in whole or in part, without the prior
//written consent of Autodesk.
//
//THE DATA IS PROVIDED "AS IS" AND WITHOUT WARRANTY. ALL WARRANTIES ARE EXPRESSLY EXCLUDED AND DISCLAIMED.
//AUTODESK MAKES NO WARRANTY OF ANY KIND WITH RESPECT TO THE DATA, EXPRESS, IMPLIED OR ARISING BY CUSTOM
//OR TRADE USAGE, AND DISCLAIMS ANY IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
//FITNESS FOR A PARTICULAR PURPOSE OR USE. WITHOUT LIMITING THE FOREGOING, AUTODESK DOES NOT WARRANT THAT
//THE OPERATION OF THE DATA WILL BE UNINTERRUPTED OR ERROR FREE.
//
//IN NO EVENT SHALL AUTODESK, ITS AFFILIATES, PARENT COMPANIES, LICENSORS OR SUPPLIERS ("AUTODESK GROUP")
//BE LIABLE FOR ANY LOSSES, DAMAGES OR EXPENSES OF ANY KIND (INCLUDING WITHOUT LIMITATION PUNITIVE OR
//MULTIPLE DAMAGES OR OTHER SPECIAL, DIRECT, INDIRECT, EXEMPLARY, INCIDENTAL, LOSS OF PROFITS, REVENUE OR
//DATA, COST OF COVER OR CONSEQUENTIAL LOSSES OR DAMAGES OF ANY KIND), HOWEVER CAUSED, AND REGARDLESS OF
//THE THEORY OF LIABILITY, WHETHER DERIVED FROM CONTRACT, TORT (INCLUDING, BUT NOT LIMITED TO, NEGLIGENCE),
//OR OTHERWISE, ARISING OUT OF OR RELATING TO THE DATA OR ITS USE OR ANY OTHER PERFORMANCE, WHETHER OR NOT
//AUTODESK HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.

#ifndef MPXHIK_H
#define MPXHIK_H

#include <assert.h>

#include <maya/MPxData.h>
#include <maya/MPxCommand.h>
#include <maya/MTypeId.h>
#include <maya/MPlug.h>  
#include <maya/MFnPluginData.h>
#include <maya/MFnNurbsCurve.h>
#include <maya/MPxClientDeviceNode.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MIOStream.h>
#include <maya/MTransformationMatrix.h>
#include <maya/MMatrix.h>
#include <maya/MVector.h>
#include <api_macros.h>

#ifndef COMPILING_MAYAHIK_IN_MAYA
	#include <humanik/humanik.h>
	#include <humanik/hikproperty.h>
#else
	#include <humanik.h>
	#include <hikproperty.h>
#endif

#include <autodeskmwkey.h>

#ifndef _DLL_
#ifdef _WINDOWS
	#define _DLL_				__declspec(dllimport) 
#else
	#define _DLL_ 
#endif
	#define EXTERN_TEMPLATE		extern
#endif

namespace MayaHIK
{

struct DestroyCh
{
	static void Release(HIKCharacter * pCharacter)
	{
		HIKCharacterDestroy(pCharacter,free);
	}
};

struct DestroyChState
{
	static void Release(HIKCharacterState * pCharacterState)
	{
		HIKCharacterStateDestroy(pCharacterState,free);
	}
};

struct DestroyProp
{
	static void Release(HIKPropertySetState * pPropState)
	{
		HIKPropertySetStateDestroy(pPropState,free);
	}
};

struct DestroyEffState
{
	static void Release(HIKEffectorSetState * pEffState)
	{
		HIKEffectorSetStateDestroy(pEffState,free);
	}
};

	template <class T, class TRelease>
	class RefCount
	{
		class Internal
		{			
			mutable int mRefCount;
		public:
			mutable T* mPtr;
			void Increment()
			{	
				mRefCount++;
			}
			void Decrement()
			{
				mRefCount--;
				if(!mRefCount)
				{
					if(mPtr != NULL)
						TRelease::Release(mPtr);
					delete this;
				}
			}

			Internal(T* ptr = 0)
			{
				mPtr = ptr;
				mRefCount = 1;
			}
			~Internal()
			{				
			}
		};

	public:		
		RefCount():mInternal(NULL){}
		RefCount(T * ptr):mInternal(new Internal(ptr)){}
		RefCount(RefCount& pCopy)
		{
			if(mInternal != NULL)
				mInternal->Decrement();
			mInternal = pCopy.mInternal;
			if(mInternal != NULL)
				mInternal->Increment();
		}
		~RefCount()
		{
			if(mInternal != NULL)
				mInternal->Decrement();

		}
		
		void operator=(const RefCount& pCopy)
		{
			if(mInternal != NULL)
				mInternal->Decrement();
			mInternal = pCopy.mInternal;
			if(mInternal != NULL)
				mInternal->Increment();
			
		}
		void operator=(T* ptr)
		{
			if(mInternal != NULL)
				mInternal->Decrement();
			mInternal = new Internal(ptr);
		}

		operator T*()
		{
			if(mInternal != NULL)
				return mInternal->mPtr;
			else
				return NULL;
		}

	private:
		Internal* mInternal;
	};

#ifdef _WINDOWS
#pragma warning(disable : 4231) // Disable "C4200: nonstandard extension used : 'extern' before template explicit instantiation"

	EXTERN_TEMPLATE template class _DLL_ RefCount<HIKCharacter, DestroyCh>;
	EXTERN_TEMPLATE template class _DLL_ RefCount<HIKCharacterState, DestroyChState>;
	EXTERN_TEMPLATE template class _DLL_ RefCount<HIKEffectorSetState, DestroyEffState>;
	EXTERN_TEMPLATE template class _DLL_ RefCount<HIKPropertySetState, DestroyProp>;

#pragma warning(default : 4200)
#endif

	class _DLL_ MPxHIKCharacter : public MPxData
	{

	public:

		MPxHIKCharacter();
		MPxHIKCharacter(HIKCharacter* pCharacter);
		virtual ~MPxHIKCharacter();

		// static methods and data.
		static const MString    typeName;
		static const MTypeId    id;
		static void* creator() {return new MPxHIKCharacter;}
	
		virtual void			copy( const MPxData& ); 
		MTypeId                 typeId() const{return id;} 
		MString					name() const{return typeName;}		
		
		RefCount<HIKCharacter, DestroyCh> mCharacter;
		bool mValid;

	};

	class _DLL_ MPxHIKCharacterState : public MPxData
	{

	public:

		MPxHIKCharacterState();
		MPxHIKCharacterState(HIKCharacterState* pState);
		virtual ~MPxHIKCharacterState();

		// static methods and data.
		static const MString    typeName;
		static const MTypeId    id;
		static void* creator() {return new MPxHIKCharacterState;}
	
		virtual void			copy( const MPxData& ); 
		MTypeId                 typeId() const{return id;} 
		MString					name() const{return typeName;}
		

		RefCount<HIKCharacterState, DestroyChState> mCharacterState;
	};


	class _DLL_ MPxHIKCharacterEffectorState : public MPxData
	{

	public:

		MPxHIKCharacterEffectorState();
		virtual ~MPxHIKCharacterEffectorState();

		// static methods and data.
		static const MString    typeName;
		static const MTypeId    id;
		static void* creator() {return new MPxHIKCharacterEffectorState;}
	
		virtual void			copy( const MPxData& ); 
		MTypeId                 typeId() const{return id;} 
		MString					name() const{return typeName;}
		
		RefCount<HIKEffectorSetState, DestroyEffState> mEffectorSetState;
	};


	class _DLL_ MPxHIKPropertySetState : public MPxData
	{

	public:

		MPxHIKPropertySetState();
		virtual ~MPxHIKPropertySetState();

		// static methods and data.
		static const MString    typeName;
		static const MTypeId    id;
		static void* creator() {return new MPxHIKPropertySetState;}
	
		virtual void			copy( const MPxData& ); 
		MTypeId                 typeId() const{return id;} 
		MString					name() const{return typeName;}
		
		RefCount<HIKPropertySetState, DestroyProp> mPropertySetState;
	};
		


	class _DLL_ MPxHIKCharacterStateClient : public MPxClientDeviceNode
	{

	public:

		MPxHIKCharacterStateClient();
		virtual ~MPxHIKCharacterStateClient();

		virtual void			postConstructor();

		virtual MStatus		compute( const MPlug& plug, MDataBlock& data );

		// static methods and data.
		static const MString    typeName;
		static const MTypeId    id;
		static void* creator() {return new MPxHIKCharacterStateClient;}
		static  MStatus		initialize();

		static MObject ICharacter;	
		static MObject IHipsScale;	
		static MObject IHipsScaleXYZ[3];	
		static MObject IReferenceGX;	
		static MObject OState;

		static  MString mICharacterAttrName;
		static  MString mIReferenceGxAttrName;
		static 	MString mOCharacterStateAttrName;

		static inline MString GetICharacterAttrName()		{ return mICharacterAttrName;}
		static inline MString GetIReferenceGxAttrName()		{ return mIReferenceGxAttrName;}
		static inline MString GetOCharacterStateAttrName()	{ return mOCharacterStateAttrName;}

		MTypeId                 typeId() const{return id;} 
		MString					name() const{return typeName;}

		void debugPrint( int counter, bool inUse, double *t, double *r, double *s );
		MStatus readUntilOneLeft( MCharBuffer& buffer );
		bool assignTRSClientDataToCharacter( const MMatrix& reference, double hipsScale[3],  MPxHIKCharacter*character, MPxHIKCharacterState* characterState );

		RefCount<HIKCharacterState, DestroyChState> mCharacterState;
	private:
		bool   mBufferValid;
		double mBuffer[ 3*(1+LastNodeId) ];
	};

	class _DLL_ MPxHIKRemoteCharacterList : public MPxCommand
	{
		public:
    
			MStatus doIt( const MArgList& );
			static MSyntax newSyntax();
			static void* creator();
			bool isUndoable() const                   { return false; }

			static const char* commandName;
	};
}

#endif
