#ifndef __NmChannel_h__
#define __NmChannel_h__

#include "imsconf3.h"

class CNmConferenceObj;

template<class T, const IID* piid, DWORD dwCh>
class ATL_NO_VTABLE CNmChannel : public T
{
friend class CNmChannelDataObj;
friend class CNmChannelAudioObj;
friend class CNmChannelVideoObj;
friend class CNmChannelFtObj;
friend class CNmChannelAppShareObj;

	public:	

	CNmChannel() : m_bActive(false), m_pConfObj(NULL) { ; }

	~CNmChannel()
	{
		DBGENTRY(CNmChannel::~CNmChannel);

		RemoveMembers();

		DBGEXIT(CNmChannel::~CNmChannel);
	}


	// INmChannel methods
	STDMETHOD(IsSameAs)(INmChannel *pChannel);
	STDMETHOD(IsActive)(void);
	STDMETHOD(SetActive)(BOOL fActive);
	STDMETHOD(GetConference)(INmConference **ppConference);
	STDMETHOD(GetInterface)(IID *_piid);
	STDMETHOD(GetNmch)(ULONG *puCh);
	STDMETHOD(EnumMember)(IEnumNmMember **ppEnum);
	STDMETHOD(GetMemberCount)(ULONG * puCount);

	// INmChannelNotify
    STDMETHOD(NmUI)(CONFN uNotify);
    STDMETHOD(MemberChanged)(NM_MEMBER_NOTIFY uNotify, INmMember *pInternalMember);
	STDMETHOD(NotifySinksOfAllMembers)()
	{
		for(int i = 0; i < m_SDKMemberObjs.GetSize(); ++i)
		{
			Fire_MemberChanged(NM_MEMBER_ADDED, m_SDKMemberObjs[i]);
		}

		return S_OK;
	}

	STDMETHOD(NotifySinksOfLocalMember)()
	{
		Fire_MemberChanged(NM_MEMBER_ADDED, GetConfObj()->GetLocalSDKMember());
		return S_OK;
	}

	virtual bool IsChannelValid()
	{
		if(m_pConfObj)
		{
			NM_CONFERENCE_STATE state;
			if(SUCCEEDED(m_pConfObj->GetState(&state)) && (NM_CONFERENCE_IDLE != state))
			{
				return true;
			}
		}
		
		return false;
	}

	virtual CNmConferenceObj* GetConfObj() { return m_pConfObj; }
	virtual BOOL GetbActive() { return m_bActive; }

	virtual void RemoveMembers()
	{

			// Free our conferencing objects
		while(m_SDKMemberObjs.GetSize())
		{
			Fire_MemberChanged(NM_MEMBER_REMOVED, m_SDKMemberObjs[0]);
			CComPtr<INmMember> sp = m_SDKMemberObjs[0];
			m_SDKMemberObjs.RemoveAt(0);
			sp.p->Release();
		}
	}

	STDMETHOD(Activate)(BOOL bActive) 
	{ 
		if(m_bActive != ((bActive) ? TRUE : FALSE))
		{
			m_bActive = ((bActive) ? TRUE : FALSE);
			_OnActivate(bActive ? true : false);
		}

		return S_OK;
	}


	STDMETHOD(SDKMemberAdded)(INmMember* pSDKMember)
	{
		HRESULT hr = S_FALSE;

		if(!_MemberInChannel(pSDKMember))
		{
				// Add the member
			pSDKMember->AddRef();
			m_SDKMemberObjs.Add(pSDKMember);

				// Notify the sinks
			Fire_MemberChanged(NM_MEMBER_ADDED, pSDKMember);
			hr = S_OK;
		}

		return hr;
	}

	STDMETHOD(SDKMemberRemoved)(INmMember* pSDKMember)
	{
		HRESULT hr = S_FALSE;

		for(int i = 0; i < m_SDKMemberObjs.GetSize(); ++i)
		{
			CComPtr<INmMember> spMember = m_SDKMemberObjs[i];

			if(spMember.IsEqualObject(pSDKMember))
			{
				m_SDKMemberObjs.RemoveAt(i);

				Fire_MemberChanged(NM_MEMBER_REMOVED, spMember);

				spMember.p->Release();

				hr = S_OK;

				break;
			}
		}

		return hr;
	}

	STDMETHOD(SDKMemberChanged)(INmMember* pSDKMember) 
	{
		return Fire_MemberChanged(NM_MEMBER_UPDATED, pSDKMember); 
	}

	void NotifySinkOfMembers(INmChannelNotify* pNotify)
	{
		for(int i = 0; i < m_SDKMemberObjs.GetSize(); ++i)
		{
			pNotify->MemberChanged(NM_MEMBER_ADDED, m_SDKMemberObjs[i]);
		}
	}

	STDMETHOD(FireNotificationsToSyncState)()
	{
		for(int i = 0; i < m_SDKMemberObjs.GetSize(); ++i)
		{
			Fire_MemberChanged(NM_MEMBER_ADDED, m_SDKMemberObjs[i]);
		}

		return S_OK;
	}

protected: // Helper Fns
	INmMember* _GetSDKMemberFromInternalMember(INmMember* pInternalMember);
	HRESULT _RemoveMember(INmMember* pInternalMember);
	virtual bool _MemberInChannel(INmMember* pSDKMember);

private:
	CSimpleArray<INmMember*>	m_SDKMemberObjs;
	CNmConferenceObj*			m_pConfObj;
	BOOL						m_bActive;
};



///////////////////////////////////////////////////////////////////
// INmChannel
///////////////////////////////////////////////////////////////////

template<class T, const IID* piid, DWORD dwCh>
STDMETHODIMP CNmChannel<T, piid, dwCh>::IsSameAs(INmChannel *pChannel)
{
	DBGENTRY(CNmChannel::IsSameAs);
	HRESULT hr = E_NOTIMPL;
	CComPtr<IUnknown> pUnk(GetUnknown());

	hr = pUnk.IsEqualObject(pChannel) ? S_OK : S_FALSE;

	DBGEXIT_HR(CNmChannel::IsSameAs,hr);
	return hr;
}

template<class T, const IID* piid, DWORD dwCh>
STDMETHODIMP CNmChannel<T, piid, dwCh>::IsActive(void)
{
	DBGENTRY(CNmChannel::IsActive);

	HRESULT hr = _IsActive();

	DBGEXIT_HR(CNmChannel::IsActive,hr);
	return hr;
}

template<class T, const IID* piid, DWORD dwCh>
STDMETHODIMP CNmChannel<T, piid, dwCh>::SetActive(BOOL fActive)
{
	DBGENTRY(CNmChannel::SetActive);

	HRESULT hr = _SetActive(fActive);

	DBGEXIT_HR(CNmChannel::SetActive,hr);
	return hr;
}

template<class T, const IID* piid, DWORD dwCh>
STDMETHODIMP CNmChannel<T, piid, dwCh>::GetConference(INmConference **ppConference)
{
	DBGENTRY(CNmChannel::GetConference);
	HRESULT hr = S_OK;

	if(m_pConfObj)
	{	
		IUnknown* pUnk = m_pConfObj->GetUnknown();
		if(pUnk)
		{
			hr = pUnk->QueryInterface(IID_INmConference,reinterpret_cast<void**>(ppConference));
		}
		else
		{
			hr = S_FALSE;
		}
	}

	DBGEXIT_HR(CNmChannel::GetConference,hr);
	return hr;
}

template<class T, const IID* piid, DWORD dwCh>
STDMETHODIMP CNmChannel<T, piid, dwCh>::GetInterface(IID *_piid)
{
	DBGENTRY(CNmChannel::GetInterface);
	HRESULT hr = S_OK;

	if(piid)
	{
		*_piid = *piid;
	}
	else
	{
		hr = E_POINTER;
	}

	DBGEXIT_HR(CNmChannel::GetInterface,hr);
	return hr;
}

template<class T, const IID* piid, DWORD dwCh>
STDMETHODIMP CNmChannel<T, piid, dwCh>::GetNmch(ULONG *puCh)
{
	DBGENTRY(CNmChannel::GetNmch);
	HRESULT hr = S_OK;

	if(puCh)
	{
		*puCh = dwCh;			
	}
	else
	{
		hr = E_POINTER;			
	}		

	DBGEXIT_HR(CNmChannel::GetNmch,hr);
	return hr;
}

template<class T, const IID* piid, DWORD dwCh>
STDMETHODIMP CNmChannel<T, piid, dwCh>::EnumMember(IEnumNmMember **ppEnum)
{
	DBGENTRY(CNmChannel::EnumMember);
	HRESULT hr = S_OK;

	hr = CreateEnumFromSimpleAryOfInterface<IEnumNmMember, INmMember>(m_SDKMemberObjs, ppEnum);

	DBGEXIT_HR(CNmChannel::EnumMember,hr);
	return hr;
}

template<class T, const IID* piid, DWORD dwCh>
STDMETHODIMP CNmChannel<T, piid, dwCh>::GetMemberCount(ULONG * puCount)
{
	DBGENTRY(CNmChannel::GetMemberCount);
	HRESULT hr = S_OK;

	if(puCount)
	{
		*puCount = m_SDKMemberObjs.GetSize();
	}
	else
	{
		hr = E_POINTER;
	}

	DBGEXIT_HR(CNmChannel::GetMemberCount,hr);
	return hr;
}

///////////////////////////////////////////////////////////////////
// INmChannelNotify
///////////////////////////////////////////////////////////////////

template<class T, const IID* piid, DWORD dwCh>
STDMETHODIMP CNmChannel<T, piid, dwCh>::NmUI(CONFN uNotify)
{
	DBGENTRY(CNmChannel::NmUI);
	HRESULT hr = S_OK;

	DBGEXIT_HR(CNmChannel::NmUI,hr);
	return hr;
}

template<class T, const IID* piid, DWORD dwCh>
STDMETHODIMP CNmChannel<T, piid, dwCh>::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pInternalMember)
{
	DBGENTRY(CNmChannel::MemberChanged);
	HRESULT hr = S_OK;

	if(pInternalMember && m_pConfObj)
	{
		INmMember* pMember = NULL;

		if(NM_MEMBER_ADDED == uNotify)
		{
				// Only add it if we haven't added it yet
			if(!(pMember = _GetSDKMemberFromInternalMember(pInternalMember)))
			{
				pMember = m_pConfObj->GetSDKMemberFromInternalMember(pInternalMember);
				ASSERT(pMember);

				pMember->AddRef();
				m_SDKMemberObjs.Add(pMember);
			}
			else
			{
					// We already have this member
				goto end;
			}
		}

		if(!pMember)
		{
			pMember = _GetSDKMemberFromInternalMember(pInternalMember);
		}

			// It is possable that the member has already been removed..
		if(pMember)
		{
			if(NM_MEMBER_REMOVED == uNotify)
			{
				_RemoveMember(pInternalMember);
			}
			else
			{
				Fire_MemberChanged(uNotify, pMember);
			}
		}
	}
	else
	{
		ERROR_OUT(("The member is not in the conference yet!!!"));
		hr = E_UNEXPECTED;
	}

end:

	DBGEXIT_HR(CNmChannel::MemberChanged,hr);
	return hr;
}


template<class T, const IID* piid, DWORD dwCh>
INmMember* CNmChannel<T, piid, dwCh>::_GetSDKMemberFromInternalMember(INmMember* pInternalMember)
{
	INmMember* pRet = NULL;

	for( int i = 0; i < m_SDKMemberObjs.GetSize(); ++i)
	{
		CComQIPtr<IInternalMemberObj> spInternal = m_SDKMemberObjs[i];
		ASSERT(spInternal);

		CComPtr<INmMember> spMember;
		if(SUCCEEDED(spInternal->GetInternalINmMember(&spMember)))
		{
			if(spMember.IsEqualObject(pInternalMember))
			{
				pRet = m_SDKMemberObjs[i];
				break;
			}
		}
	}

	return pRet;
}


template<class T, const IID* piid, DWORD dwCh>
HRESULT CNmChannel<T, piid, dwCh>::_RemoveMember(INmMember* pInternalMember)
{
	HRESULT hr = E_FAIL;

	for( int i = 0; i < m_SDKMemberObjs.GetSize(); ++i)
	{
		CComQIPtr<IInternalMemberObj> spSDKObj = m_SDKMemberObjs[i];
		CComPtr<INmMember> spSDKMember = m_SDKMemberObjs[i];
		ASSERT(spSDKObj);

		CComPtr<INmMember> spMember;
		if(SUCCEEDED(spSDKObj->GetInternalINmMember(&spMember)))
		{
				// Worst case we return S_FALSE to indicate that there is no such member
			hr = S_FALSE;

			if(spMember.IsEqualObject(pInternalMember))
			{
				m_SDKMemberObjs.RemoveAt(i);

				Fire_MemberChanged(NM_MEMBER_REMOVED, spSDKMember);

				spSDKMember.p->Release();
				hr = S_OK;
				break;
			}
		}
	}

	return hr;	
}

template<class T, const IID* piid, DWORD dwCh>
bool CNmChannel<T, piid, dwCh>::_MemberInChannel(INmMember* pSDKMember)
{
	for( int i = 0; i < m_SDKMemberObjs.GetSize(); ++i)
	{
		
		CComPtr<INmMember> spMember = m_SDKMemberObjs[i];
		if(spMember.IsEqualObject(pSDKMember))
		{
			return true;
		}
	}

	return false;
}



#endif // __NmChannel_h__