
/*
  File: include/mia/mobject.h
  Description: MIA aggregate object helper
  Part of the Mantis project
  (c) 2002 Lycos Inc.

  This file is distributed for the sole purpose of allowing you to write
  software to interface with Sonique 2 and other Mantis programs. It is
  not to be modified or used for any other purpose without written
  authorization.

  If you redistribute this file, it must be in the original package
  complete with all other files provided, including the usage agreement.

  Do not remove this notice.
*/

#ifndef _MIA_MOBJECT_H__
#define _MIA_MOBJECT_H__

#include "MIAInternals.h"
#include "mgeneric.h"
#include "HeapPtr.h"

// {5A7F3DEB-6D89-4645-9A56-704421EE3E89}
DEFINE_MID( MID_MOBJECT,			0x5a7f3deb, 0x6d89, 0x4645, 0x9a, 0x56, 0x70, 0x44, 0x21, 0xee, 0x3e, 0x89);

#ifdef _DEBUG
inline bool ExistsInInnerGenericArray( Array< RefPtr<MInnerGeneric>, 1 >& innerArray, MInnerGeneric* pObj )
{
	uint32 nCount = innerArray.GetCount();
	while(nCount--)
	{
		if( innerArray[nCount] == pObj )
			return true;
	}
	return false;
}
#endif

#define INNERRELEASE(x) x->InnerRelease();


#define MOBJECTIMP_DEFMACRO_()\
protected:\
	Array< RefPtr<MInnerGeneric>, 1 > m_InnerGenericArray;\
public:\
	virtual MRESULT __stdcall AddObject( MInnerGeneric* pObj )\
	{\
		if( pObj )\
		{\
			ASSERT( !ExistsInInnerGenericArray( m_InnerGenericArray, pObj ) );\
			m_InnerGenericArray.AddTail();\
			m_InnerGenericArray.Tail() = pObj;\
			return M_OK;\
		}\
		return -1;\
	}\
	virtual MRESULT __stdcall QueryModule(const MID &iid, void **ppv, bool bAddReference = true, const MGeneric* pPrev = NULL) \
	{\
		ASSERT(ppv);\
		*ppv = NULL;\
		if( iid == MID_MOBJECT )\
			*ppv = (MObject*)this;\
		else\
		{\
			uint32 nCount = m_InnerGenericArray.GetCount();	\
			if( pPrev )\
			{\
				while( *ppv != (void*)pPrev && nCount--)\
				{\
					*ppv = m_InnerGenericArray[nCount]->InnerCast(iid);\
				}\
				if( *ppv == (void*)pPrev )\
				{\
					*ppv = NULL;\
				}\
				else\
					return -1;\
			}\
			while( !*ppv && nCount--)\
			{\
				*ppv = m_InnerGenericArray[nCount]->InnerCast(iid);\
			}\
		}\
		if( *ppv )\
		{\
			if( bAddReference )\
				((MGeneric*)*ppv)->AddRef();\
			return M_OK;\
		}\
		return -1;\
	}\
	virtual MRESULT __stdcall AddObject(	const MID& mIFactory,const MID& mInterface)\
	{\
		MInnerGeneric* pThingy;\
		unsigned long ret = MCreateInstance( mIFactory, mInterface, (void**)&pThingy, this );\
		if( SUCCEEDED( ret ) )\
		{\
			AddObject( pThingy );\
			INNERRELEASE(pThingy);/*pThingy->InnerRelease();*/  /* one ref is enough */\
			return M_OK;\
		}\
		return -1;\
	}	


#define MOBJECTIMP_DEFMACRO(x) MOBJECTIMP_DEFMACRO_() IMPLEMENT_REFERENCECOUNT1(x)

class MObjectImp : public MObject
{
protected:
	Array< RefPtr<MInnerGeneric>, 1 > m_InnerGenericArray;
public:
	MObjectImp()
	{ 
		MGENERIC_CONSTRUCTOR();
	}

	virtual ~MObjectImp(){};

	virtual MRESULT __stdcall AddObject( MInnerGeneric* pObj )
	{
		if( pObj )
		{
			ASSERT( !ExistsInInnerGenericArray( m_InnerGenericArray, pObj ) );
			m_InnerGenericArray.AddTail();
			m_InnerGenericArray.Tail() = pObj;
			return M_OK;
		}
		return -1;
	}

	virtual MRESULT __stdcall QueryModule(const MID &iid, void **ppv, bool bAddReference = true, const MGeneric* pPrev = NULL) 
	{
		ASSERT(ppv);
		*ppv = NULL;

		if( iid == MID_MOBJECT )
			*ppv = (MObject*)this;
		else
		{
			int32 nCount = m_InnerGenericArray.GetCount();

			if( pPrev )
			{
				while( *ppv != (void*)pPrev && nCount--)
				{
					*ppv = m_InnerGenericArray[nCount]->InnerCast(iid);
				}

				if( *ppv == (void*)pPrev )
				{
					*ppv = NULL;
				}
				else
					return -1;
			}

			while( !*ppv && nCount--)
			{
				*ppv = m_InnerGenericArray[nCount]->InnerCast(iid);
			}
		}

		if( *ppv )
		{
			if( bAddReference )
				((MGeneric*)*ppv)->AddRef();

			return M_OK;
		}

		return -1;
	}

	virtual MRESULT __stdcall AddObject(	const MID& mIFactory,const MID& mInterface )
	{
		MInnerGeneric* pThingy;
		unsigned long ret = MCreateInstance( mIFactory, mInterface, (void**)&pThingy, this );
		if( SUCCEEDED( ret ) )
		{
			AddObject( pThingy );

			pThingy->InnerRelease(); /* one ref is enough */
			return M_OK;
		}
		return -1;
	}

	IMPLEMENT_REFERENCECOUNT()
};
#endif
