/****************************************************************************************


		MCCountedPtr.h


		Copyright: (c) 2001 Eovia Corp. All rights reserved.


		Author:	Greg (from altbase.h in Microsoft Visual C++ 5.0)
		Date:	4/15/98

		Revision History

		when		who			what & why
		==========	==========	======================================================
		10/19/99	Charles		Put the fObject to null in the destructor of TMCCountedPtr
		9/23/99		Ian			Got rid of MCMemory.h it wasn't needed.
		09/14/99	Jerome		Debugged the refcounting tools. There were problems with
								the test in ReleaseIfNotNull
		9/11/99		Ian			Bug in ReleaseIfNotNull() needed to use &ptr when checking
								for a counted ptr.
		9/2/99		Ian			Added a context to the TMCCountedPtr. This context is used
								when we record a stack reference. This solves the problem
								of TMCCountedPtrs that are declared in a block of memory that
								may move (like a class array). Also added two different
								Adds and Reomves for the stack references, one is for when
								you have a counted ptr and hence a CountedPtrContext, and one
								for when you just have a pointer.
		06/17/99	Eml			Added const operator&, for more const-istency with C++ normal pointer const-itution
		6/10/99		Ian			Array API changes.
		4/23/99		Ian			Fixed TMCCountedGetHelper/TMCCountedCreateHelper, needed to pass
								the T** as the context, not this.
		4/23/99		psq			added TMCCountedGetHelper/TMCCountedCreateHelper. This should allow
								much better tracking of AddRef/Release with qRecordCountedPtrRefs
		3/16/99		Ian			Made all the stack reference code take const void*'s.
								Added a GetElem() for TMCCountedPtrArray<>.
		03/02/99	dsm			formated some inline functions on one line for debug purpose
		2/25/99		tsh			fix date for people in USA
		2/25/99		Alec		fixed typo in array ctor, I guess no one in either app
									ever sets the size,max count,growth rate in the ctor - that's scary
		1/19/99		psq			fixed TMCCountedPtrArray::operator[]. The previous implementation
								ended up constructing a stack variable and returning a reference
								on the stack. That didn't work in optimized form.
		12/16/98	Ian			Made sure that values passed to QueryInterface() are cleared out.
								API Change: TMCCountedPtrArray::operator[] now returns a
								TMCCountedPtr<T>&, you may need to do some casting to fix compile
								errors.
		12/4/98		Ian			Did some sanity checking in the stack recording code, make
								sure QuesryInterface actually returns something.
		12/3/98		Ian			QueryInterface() wants a void**, had to cast IMCUnknown** to void**.
		11/25/98	Ian			Added a copy constructor and operator= to TMCCountedPtrArray.
		11/18/98	cph			fixed typo in TMCCountedPtrArray constructor
		11/5/98		Ian			Fixed a compile error Pascal didn't see 'cause he has
								qRecordCountedPtrRefs off.
		10/14/98	psq			put qRecordCountedPtrRefs back. Too slow and causes problems in
								all extensions.
								Called Release on result of query interface instead
		10/14/98	Ian			Got rid of the qRecordCountedPtrRefs checks.
								Now use QueryInterface to get an object to record for stack checking
								Had to add a destructor for TMCCountedPtrArray().
		9/23/98		psq			modified #ifdef around logging
		9/9/98		Ian			Removed the qRecordCountedPtrRefs tests, we test that in
								the source file now. Fixed TMCCountedPtrArray, we didn't
								need to override AddElem() or InsertElem(). Added override
								of ArrayFree().
		9/8/98		Ian			Added code to record stack frames for counted pointers.
								Fixed TMCCountedPtrArray function declarations to match
								the inherited virtual calls.
		9/3/98		psq			massive overhaul of the list, changed class hierarchy
		8/24/98		psq			added more checks in assigned
								simplified "#if defined(__MWERKS__) && (__MWERKS__ < 0x2200)"
		8/21/98		Ian			Added temporary conditional compile tests for the Call* stuff.
		8/11/98		psq			~TMCCountedPtr & Release made safer
		7/5/98		Ian			Replaces MAC test with __MWERKS__.
		6/10/98		Joshua		Removed lame assert from operator&
		6/5/98		Ian			Added a tests for 0xDECEA5ED, which is what we fill our
								deleted memory with.
		06/03/98	Greg		Fixed Mac problem with default copy constructors and
								operator= (I had to outline some calls)
		06/02/98	Greg		Fixed compiler bug in operator=, added some two operator=='s,
								added TMCCountedArray
		05/22/98	Greg		Renamed to TMCCountedPtr
		04/15/98	PBK			did some early cleanup
					
*****************************************************************************************/

#ifndef __MCCOUNTEDPTR__
#define __MCCOUNTEDPTR__

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "MCAssert.h"
#include "MCBasicDefines.h"
#include "MCCompObj.h"
#include "MCCompiler.h"
#include "MCException.h"
#include "MCBasicGuid.h"



#if defined(__MWERKS__) && (__MWERKS__ < 0x2200)
void CallAddRef(void* object);
void CallRelease(void* object);
#endif

#if Debug_  && qRecordCountedPtrRefs
typedef uint32 CountedPtrContext;

//This tag identifies a TMCCountedPtr<> instance.
#define kCountedPtrTag 'CPtr'

//Check if this IMCUnknown* / TMCCountedObject*, is really a TMCCountedPtr<> instance.
#define macroIsCountedPtr(_possibleCP)	(*(((uint32*)(_possibleCP)) + 2) == kCountedPtrTag)

//If this is a TMCCountedPtr<> instance, this will get the CountedPtrContext from it.
#define macroRetreiveCPContext(_CP)		((CountedPtrContext)(*(((uint32*)(_CP)) + 1)))

extern CountedPtrContext	GetNewCountedPtrContext	(void);
extern void					ReleaseCountedPtrContext(CountedPtrContext& ioContext);


/*****************************************************************************\
*                                                                             *
*  TMCStrackRecorder                                                          *
*                                                                             *
\*****************************************************************************/

class TMCStackRecorder : public TMCObject
{
public:
	static void		AddReference		(const void* object, const void* context);
	static void		RemoveReference		(const void* object, const void* context);
	static uint16	GetHashedIndex		(const void* inValue);
	static boolean* gIsInStackReference;
	static boolean*	gIsReferencingOn;	// You can set this global variable to on or off to disable references temporarily. You can set it either by code or directly in your debugger
};

#if Debug_ && qRecordCountedPtrRefs

//Use these when you have a CountedPtrContext.
template<class T> inline void AddCPStackReference(const T* object, const CountedPtrContext context)
{
	if (!TMCStackRecorder::gIsReferencingOn) return;
	if (*TMCStackRecorder::gIsReferencingOn == false) return;
	if (!TMCStackRecorder::gIsInStackReference) return;
	if (*TMCStackRecorder::gIsInStackReference) return;
	*TMCStackRecorder::gIsInStackReference = true;
	IMCUnknown* realObject = NULL;
	((T*)object)->QueryInterface(IID_IMCUnknown, (void**)&realObject);
	if(MCVerify(realObject))
	{
		TMCStackRecorder::AddReference(realObject, (void*)context);
		realObject->Release();
	}
	*TMCStackRecorder::gIsInStackReference = false;
}

template<class T> inline void RemoveCPStackReference(const T* object, const CountedPtrContext context)
{
	if (!TMCStackRecorder::gIsReferencingOn) return;
	if (*TMCStackRecorder::gIsReferencingOn == false) return;
	if (!TMCStackRecorder::gIsInStackReference) return;
	if (*TMCStackRecorder::gIsInStackReference) return;
	*TMCStackRecorder::gIsInStackReference = true;
	IMCUnknown* realObject = NULL;
	((T*)object)->QueryInterface(IID_IMCUnknown, (void**)&realObject);
	if(MCVerify(realObject != NULL))
	{
		TMCStackRecorder::RemoveReference(realObject, (void*)context);
		realObject->Release();
	}
	*TMCStackRecorder::gIsInStackReference = false;
}

//Use these when you just have a ptr.
template<class T> inline void AddPStackReference(const T* object, const void* context)
{
	if (!TMCStackRecorder::gIsReferencingOn) return;
	if (*TMCStackRecorder::gIsReferencingOn == false) return;
	if (!TMCStackRecorder::gIsInStackReference) return;
	if (*TMCStackRecorder::gIsInStackReference) return;
	*TMCStackRecorder::gIsInStackReference = true;
	IMCUnknown* realObject = NULL;
	((T*)object)->QueryInterface(IID_IMCUnknown, (void**)&realObject);
	if(MCVerify(realObject))
	{
		TMCStackRecorder::AddReference(realObject, context);
		realObject->Release();
	}
	*TMCStackRecorder::gIsInStackReference = false;
}

template<class T> inline void RemovePStackReference(const T* object, const void* context)
{
	if (!TMCStackRecorder::gIsReferencingOn) return;
	if (*TMCStackRecorder::gIsReferencingOn == false) return;
	if (!TMCStackRecorder::gIsInStackReference) return;
	if (*TMCStackRecorder::gIsInStackReference) return;
	*TMCStackRecorder::gIsInStackReference = true;
	IMCUnknown* realObject = NULL;
	((T*)object)->QueryInterface(IID_IMCUnknown, (void**)&realObject);
	if(MCVerify(realObject != NULL))
	{
		TMCStackRecorder::RemoveReference(realObject, context);
		realObject->Release();
	}
	*TMCStackRecorder::gIsInStackReference = false;
}

#endif //#if Debug_ && qRecordCountedPtrRefs

/*****************************************************************************\
*                                                                             *
*  TMCPassiveStackRecorder                                                    *
*  -----------------------                                                    *
*                                                                             *
*  Include this class in your object and call AddReference()                  *
*     in the member you want to track the callers                             *
*                                                                             *
\*****************************************************************************/


class TMCPassiveStackRecorder : public TMCObject
{
public:
	TMCPassiveStackRecorder();
	~TMCPassiveStackRecorder();

	void AddReference();

protected:
	class TStackReferenceObject* fReferences;
};

#else

/// used to track leaks, not available for SDK extensions
///
class TMCPassiveStackRecorder : public TMCObject
{
public:
	TMCPassiveStackRecorder() {}
	~TMCPassiveStackRecorder() {}

	void AddReference() {}

};

#endif

inline void ReleaseIfNotNull(void*& ptr)
{
	if (ptr)
	{
#if Debug_ && qRecordCountedPtrRefs
		if(macroIsCountedPtr(&ptr))
		{
			RemoveCPStackReference<IMCUnknown>(
				reinterpret_cast<const IMCUnknown*>(ptr),
				macroRetreiveCPContext(&ptr));
		}
		else
		{
			RemovePStackReference<IMCUnknown>(
				reinterpret_cast<const IMCUnknown*>(ptr),
				&ptr);
		}
#endif
		(reinterpret_cast<IMCUnknown*>(ptr))->Release();
		ptr = NULL;
	}
}

template<class T> inline void ReleaseIfNotNull(T** ptr)
{
	if (MCVerify(ptr) && *ptr)
	{
#if Debug_ && qRecordCountedPtrRefs
		if(macroIsCountedPtr(ptr))
		{
			RemoveCPStackReference<T>(
				reinterpret_cast<T*>(*ptr),
				macroRetreiveCPContext(ptr));
		}
		else
		{
			RemovePStackReference<T>(*ptr, ptr);
		}
#endif
		(*ptr)->Release();
		(*ptr) = NULL;
	}
}


/// \addtogroup MCCore
/// \{

///	<P>Counted objects are objects that use a counter to keep track of how many other objects need them, 
/// and delete themselves when nobody needs them anymore. A counted object has an AddRef method that increments its counter, 
/// and a Release method that decrements it. When the value of the counter reaches zero, the object is deleted.</P>
///
/// <P>It is possible to call AddRef and Release "by hand" but it is a bad idea for at least two reasons. 
/// First, it is very easy to forget to add a call to Release, therefore causing a memory leak.
/// Second the code becomes more complex because in every function that calls AddRef, you must consider exceptions.
/// So you have to use a try/catch block, add a call to Release at the end of the try AND another one 
/// before re-throwing in the catch section...</P>
///
///	<P>Because they automatically call AddRef and Release, TMCCountedPtr, TMCCountedGetHelper 
/// and TMCCountedCreateHelper are your best friends.</P>
/// 
/// <P>TMCCountedPtr overrides many operators (including *, ->, &, =, and cast) so that you can use it like other pointers
/// (the difference being the calls to AddRef and Release when you assign the pointer). Here is a pseudo code sample: </P>
/// \code
///	void CountedPointersSample()
/// {
///		TMCCountedPtr<IMCFile> file;				//a counted pointer to a file object (initialized to NULL)
///		gFileUtilities->CreateIMCFile(&file);		//ask the application to create a file (its refcount will be 1)
///		IMCFile* file2= pfile;						//that's legal (but doesn't addref)
///		if (MCVerify(file))							//just to illustrate a test with an assert
///		{
///			TMCCountedPtr<IMCFile> file3;
///			file3= file2;							//the refcount becomes 2 because file3 is a counted pointer
///		}											//the refcount becomes 1 as the destructor of file3 is called
///		file = NULL;								//the refcount becomes 0 and the file object is deleted.
///		//at this point file2 is invalid. Lets set it to null so nobody corrupts the memory by trying to use it.
///		file2= NULL;
///	}
/// \endcode
///
///	\note When using counted pointers as class members, you must be careful and avoid creating cycles 
/// (two or more objects pointing to each other). If you have cycles, the objects will never be deleted...
///
/// For more information, please refer to the "Counted Pointers" chapter in the overview of the SDK.
///
template <class T> class TMCCountedPtr : public TMCObject0
{
	public:

		TMCCountedPtr() 
		{
#if Debug_ && qRecordCountedPtrRefs
			fContext = GetNewCountedPtrContext();
			fTag = kCountedPtrTag;
#endif
			fObject = NULL;
		}
		
		explicit TMCCountedPtr(T* lp)
		{
#if Debug_ && qRecordCountedPtrRefs
			fContext = GetNewCountedPtrContext();
			fTag = kCountedPtrTag;
#endif
			MCAssert(lp != (T*)0xDECEA5ED);
			if ((fObject = lp) != NULL)
			{
				fObject->AddRef();
#if Debug_ && qRecordCountedPtrRefs
				AddCPStackReference<T>(fObject, fContext);
#endif
			}
		}
				
	
		TMCCountedPtr(const TMCCountedPtr<T>& lp)
		{
#if Debug_ && qRecordCountedPtrRefs
			fContext = GetNewCountedPtrContext();
			fTag = kCountedPtrTag;
#endif
			MCAssert(lp.fObject != (void*)0xDECEA5ED);
			if ((fObject = lp.fObject) != NULL)
			{
#if defined(__MWERKS__) && (__MWERKS__ < 0x2200)
				CallAddRef(fObject);
#else
				fObject->AddRef();
#endif
#if Debug_ && qRecordCountedPtrRefs
				AddCPStackReference<T>(fObject, fContext);
#endif
			}
		}

		~TMCCountedPtr() 
		{
			MCAssert(fObject != (void*)0xDECEA5ED);
			if (fObject)
			{ 
				MCAssert(*(void**)fObject != (void*)0xDECEA5ED);
#if Debug_ && qRecordCountedPtrRefs
				RemoveCPStackReference<T>(fObject, fContext);
#endif
				fObject->Release();
				fObject=NULL;
			}
#if Debug_ && qRecordCountedPtrRefs
			ReleaseCountedPtrContext(fContext);
			fTag = 0xDECEA5ED;
#endif
		}

		uint32 Release() 
		{
			MCAssert(fObject != (void*)0xDECEA5ED);
			uint32 outRefs = 0;
			if (fObject)
			{
				MCAssert(*(void**)fObject != (void*)0xDECEA5ED);
#if Debug_ && qRecordCountedPtrRefs
				RemoveCPStackReference<T>(fObject, fContext);
#endif
				outRefs = fObject->Release();
			}
			fObject=NULL;
			return outRefs;
		}
		
						
		operator T*() const
		{ MCAssert(fObject != (void*)0xDECEA5ED); return (T*)fObject; }
		
		T& operator*() const
		{ MCAssertPointerCheck(fObject); return *fObject; }

		
		static T* Assign(T** pp, T* lp)
		{
			MCAssertPointerCheck(pp);
			MCAssert(*pp != (void*)0xDECEA5ED);
			MCAssert((lp == NULL) || (lp != (void*)0xDECEA5ED));
			MCAssert((lp == NULL) || (*(void**)lp != (void*)0xDECEA5ED));
			if (lp == *pp) return lp;

			if (lp != NULL)
			{
#if defined(__MWERKS__) && (__MWERKS__ < 0x2200)
				CallAddRef(lp);
#else
				lp->AddRef();
#endif
			}

			if (*pp)
			{
#if defined(__MWERKS__) && (__MWERKS__ < 0x2200)
				CallRelease(*pp);
#else
				(*pp)->Release();
#endif
			}
			*pp = lp;
			return lp;
		}

		T** operator&() 
		{ return &fObject; }

		T*const* operator&() const
		{ return &fObject; }
		
		TMCCountedPtr<T>* GetAddress()
		{
			return this;
		}

		T* operator->() const
		{ MCAssertPointerCheck(fObject); return fObject; }
		
		T* operator=(T* lp)
		{
#if Debug_ && qRecordCountedPtrRefs
			if(fObject) RemoveCPStackReference<T>(fObject, fContext);
#endif
			T* result = (T*)Assign(&fObject, lp);
#if Debug_ && qRecordCountedPtrRefs
			if(fObject) AddCPStackReference<T>(fObject, fContext);
#endif
			return result;
		}
		
		T* operator=(const TMCCountedPtr<T>& lp)
		{
#if Debug_ && qRecordCountedPtrRefs
			if(fObject) RemoveCPStackReference<T>(fObject, fContext);
#endif
			T* result = (T*)Assign(&fObject, lp.fObject);
#if Debug_ && qRecordCountedPtrRefs
			if(fObject) AddCPStackReference<T>(fObject, fContext);
#endif
			return result;
		}

		boolean operator==(const TMCCountedPtr<T>& rhs) const
		{ return (((T *)rhs) == fObject); }

		boolean operator==(const T* rhs) const
		{ return (rhs == fObject); }
		
		boolean operator==(T* rhs) const
		{ return (rhs == fObject); }


#if _MSC_VER > 1020 || __MWERKS__ || __GNUC__
		bool operator!(){	return (fObject == NULL);}
#else
		BOOL operator!(){	return (fObject == NULL) ? TRUE : FALSE;}
#endif

		void CheckLastRef(void)
		{
#if Debug_
			fObject->AddRef();
			uint refCount = fObject->Release();
			if(refCount != 1)
			{
				MCNotify("this pointer is supposed to be the last reference");
#if qRecordCountedPtrRefs
				// the next line will assert : (conntext alread exist)
				// at this point, you will see what other contexts are leaking
				//if (refCount > 1) AddStackReference<T>(fObject, this);
				
				// This is better to find the references :-)
				ExploreStackReferencesIU(fObject);
#endif
			}
#endif
		}

		T*					fObject;

#if qRecordCountedPtrRefs
		CountedPtrContext	fContext;
		uint32				fTag;
#endif
};

/// \}


#if Debug_ && qRecordCountedPtrRefs

// This is a convenient routine intended to ease finding leaks by simply parsing the data structures,
// so you do not have to mess with your debugger too much

// Explore all references of all objects
void ExploreAllStackReferences	();

// Find an object in the stak references and explore its references
void ExploreStackReferences		(void* object);
void ExploreStackReferencesIU	(void* object);	// This function will call ExploreStackReferences after
												// a queryinterface to get an IMCUnknown. Usefull for the
												// IMCUnknown refcounting

#endif



#endif