/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    confpart.cpp

Abstract:

    This module contains implementation of the participant classes.

Author:

    Mu Han (muhan)   15-September-1999

--*/

#include "stdafx.h"
#include "common.h"
#include "confpart.h"

#ifdef DEBUG_REFCOUNT

ULONG CParticipant::InternalAddRef()
{
    ULONG lRef = CComObjectRootEx<CComMultiThreadModelNoCS>::InternalAddRef();

    LOG((MSP_TRACE, "%p, %s Addref, ref = %d", 
        this, (m_InfoItems[0]) ? m_InfoItems[0] : "new participant", lRef));

    return lRef;
}

ULONG CParticipant::InternalRelease()
{
    ULONG lRef = CComObjectRootEx<CComMultiThreadModelNoCS>::InternalRelease();
    
    LOG((MSP_TRACE, "%p, %s Release, ref = %d", 
        this, (m_InfoItems[0]) ? m_InfoItems[0] : "new participant", lRef));

    return lRef;
}
#endif

CParticipant::CParticipant()
    : m_pFTM(NULL),
      m_dwSendingMediaTypes(0),
      m_dwReceivingMediaTypes(0)
{
    // initialize the info item array.
    ZeroMemory(m_InfoItems, sizeof(char *) * (RTCP_SDES_LAST - 1));
}

// methods called by the call object.
HRESULT CParticipant::Init(
    IN  char *              szCName,
    IN  ITStream *          pITStream, 
    IN  DWORD               dwSSRC,
    IN  DWORD               dwSendRecv,
    IN  DWORD               dwMediaType
    )
/*++

Routine Description:

    Initialize the participant object.

Arguments:
    
    szCName - the canonical name of the participant.

    pITStream - the stream that has the participant.

    dwSSRC - the SSRC of the participant in that stream.

    dwSendRecv - a sender or a receiver.

    dwMediaType - the media type of the participant.

Return Value:

    S_OK,
    E_OUTOFMEMORY.

--*/
{
    LOG((MSP_TRACE, "CParticipant::Init, name:%s", szCName));

    // create the marshaler.
    HRESULT hr;
    hr = CoCreateFreeThreadedMarshaler(GetControllingUnknown(), &m_pFTM);
    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "create marshaler failed, %x", hr));
        return hr;
    }

    m_InfoItems[0] = (char *)malloc(lstrlenA(szCName) + 1);
    if (m_InfoItems[0] == NULL)
    {
        LOG((MSP_ERROR, "out of mem for CName"));
        return E_OUTOFMEMORY;
    }

    lstrcpyA(m_InfoItems[0], szCName);

    // add the stream into out list.
    hr = AddStream(pITStream, dwSSRC, dwSendRecv, dwMediaType);

    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "failed to add stream %x", hr));
        return hr;
    }

    LOG((MSP_TRACE, "CParticipant: %s, Init returns S_OK", szCName));
    return S_OK;
}

BOOL CParticipant::UpdateInfo(
    IN  int                 Type,
    IN  DWORD               dwLen,
    IN  char *              szInfo
    )
/*++

Routine Description:

    Update one item of the participant info.

Arguments:
    
    Type - the type of the INFO, 
    
    dwLen - the length of the information.

    szInfo - the information.

Return Value:

    TRUE - information changed.

    FALSE - the information is the same, no change was made.

--*/
{
    int index = Type - 1;

    // if we have an item already, find out if it is the same.
    if (m_InfoItems[index] != NULL)
    {
        if (lstrcmpA(m_InfoItems[index], szInfo) == 0)
        {
            return FALSE;
        }

        // if the item is new, free the old one
        free(m_InfoItems[index]);
    }

    // allocate memory and store it.
    m_InfoItems[index] = (char *)malloc(dwLen + 1);
    if (m_InfoItems[index] == NULL)
    {
        return FALSE;
    }

    lstrcpynA(m_InfoItems[index], szInfo, dwLen);

    return TRUE;
}

BOOL CParticipant::UpdateSSRC(
    IN  ITStream *      pITStream, 
    IN  DWORD           dwSSRC,
    IN  DWORD           dwSendRecv
    )
/*++

Routine Description:

    Update the SSRC for a stream.

Arguments:
    
    pITStream - the stream that the participant is on.

    dwSSRC - the SSRC of the participant.

    dwSendRecv - the participant is a sender or a receiver.

Return Value:

    TRUE - information changed.

    FALSE - the stream is not found.

--*/
{
    CLock lock(m_lock);

    // if the stream is already there, update the SSRC and return.
    int index = m_Streams.Find(pITStream);
    if ( index >= 0)
    {
        m_StreamInfo[index].dwSSRC  = dwSSRC;
        m_StreamInfo[index].dwSendRecv |= dwSendRecv;
        return TRUE;
    }

    return FALSE;
}

BOOL CParticipant::HasSSRC(
    IN  ITStream *      pITStream, 
    IN  DWORD           dwSSRC
    )
/*++

Routine Description:

    find out if the participant has the SSRC for a stream.

Arguments:
    
    pITStream - the stream that the participant is on.

    dwSSRC - the SSRC of the participant.

Return Value:

    TRUE - the SSRC exists.

    FALSE - the SSRC does not exist.

--*/
{
    CLock lock(m_lock);

    int index = m_Streams.Find(pITStream);
    if (index >= 0)
    {
        return (m_StreamInfo[index].dwSSRC == dwSSRC);
    }

    return FALSE;
}

BOOL CParticipant::GetSSRC(
    IN  ITStream *      pITStream, 
    OUT DWORD  *        pdwSSRC
    )
/*++

Routine Description:

    Update the SSRC for a stream.

Arguments:
    
    pITStream - the stream that the participant is on.

    pdwSSRC - the address to store the SSRC of the participant.

Return Value:

    TRUE - the SSRC is found.

    FALSE - the SSRC is not found.

--*/
{
    CLock lock(m_lock);

    // if the stream is already there, update the SSRC and return.
    int index = m_Streams.Find(pITStream);
    if ( index >= 0)
    {
        *pdwSSRC = m_StreamInfo[index].dwSSRC;
        return TRUE;
    }

    return FALSE;
}

DWORD CParticipant::GetSendRecvStatus(
    IN  ITStream *      pITStream
    )
/*++

Routine Description:

    find out the current send and recv status on a given stream.

Arguments:
    
    pITStream - the stream that the participant is on.

Return Value:

    A bit mask of send and receive status

--*/
{
    CLock lock(m_lock);

    int index = m_Streams.Find(pITStream);
    if (index >= 0)
    {
        return m_StreamInfo[index].dwSendRecv;
    }

    return 0;
}

void CParticipant::FinalRelease()
/*++

Routine Description:

    release everything before being deleted. 

Arguments:
    
Return Value:

--*/
{
    LOG((MSP_TRACE, "CParticipant::FinalRelease, name %s", m_InfoItems[0]));

    if (m_pFTM)
    {
        m_pFTM->Release();
    }
    
    for (int i = 0; i < RTCP_SDES_LAST - 1; i ++)
    {
        if (m_InfoItems[i])
        {
            free(m_InfoItems[i]);
        }
    }

    for (i = 0; i < m_Streams.GetSize(); i ++)
    {
        m_Streams[i]->Release();
    }
    m_Streams.RemoveAll();

    LOG((MSP_TRACE, "CParticipant::FinalRelease - exit"));
}


// ITParticipant methods, called by the app.
STDMETHODIMP CParticipant::get_ParticipantTypedInfo(
    IN  PARTICIPANT_TYPED_INFO  InfoType,
    OUT BSTR *                  ppInfo
    )
/*++

Routine Description:

    Get a information item for this participant.

Arguments:
    
    InfoType - The type of the information asked.

    ppInfo  - the mem address to store a BSTR.

Return Value:

    S_OK,
    E_INVALIDARG,
    E_POINTER,
    E_OUTOFMEMORY,
    TAPI_E_NOITEMS
*/
{
    LOG((MSP_TRACE, "CParticipant get info, type:%d", InfoType));
    
    if (InfoType > PTI_PRIVATE || InfoType < 0)
    {
        LOG((MSP_ERROR, "CParticipant get info - exit invalid arg"));
        return E_INVALIDARG;
    }

    if (IsBadWritePtr(ppInfo, sizeof(BSTR)))
    {
        LOG((MSP_ERROR, "CParticipant get info - exit E_POINTER"));
        return E_POINTER;
    }

    // check if we have that info.
    CLock lock(m_lock);
    
    int index = (int)InfoType; 
    if (m_InfoItems[index] == NULL)
    {
        LOG((MSP_INFO, "CParticipant get info - no item for %d", InfoType));
        return TAPI_E_NOITEMS;
    }

    // conver the char string to WCHAR string.
    WCHAR Buffer[RTP_MAX_SDES + 1];
    
    if (!MultiByteToWideChar(
        GetACP(),
        0,
        m_InfoItems[index],
        -1,
        Buffer,
        RTP_MAX_SDES
        ))
    {
        LOG((MSP_ERROR, "coverting failed, error:%x", GetLastError()));
        return E_FAIL;
    }

    // make a BSTR out of it.
    BSTR pName = SysAllocString(Buffer);

    if (pName == NULL)
    {
        LOG((MSP_ERROR, "CParticipant get info - exit out of mem"));
        return E_OUTOFMEMORY;
    }

    // return the BSTR.
    *ppInfo = pName;

    return S_OK; 
}

STDMETHODIMP CParticipant::get_MediaTypes(
//    IN  TERMINAL_DIRECTION  Direction,
    OUT long *  plMediaTypes
    )
/*++

Routine Description:

    Get the media type of the participant

Arguments:
    
    plMediaType - the mem address to store a long.

Return Value:

    S_OK,
    E_POINTER,
*/
{
    LOG((MSP_TRACE, "CParticipant::get_MediaTypes - enter"));

    if (IsBadWritePtr(plMediaTypes, sizeof (long)))
    {
        LOG((MSP_ERROR, "CParticipant::get_MediaType - exit E_POINTER"));

        return E_POINTER;
    }

    CLock lock(m_lock);

#if 0
    if (Direction == TD_RENDER)
    {
        *plMediaTypes = (long)m_dwReceivingMediaTypes;
    }
    else
    {
        *plMediaTypes = (long)m_dwSendingMediaTypes;
    }
#endif

    *plMediaTypes = (long)(m_dwSendingMediaTypes | m_dwReceivingMediaTypes);

    LOG((MSP_TRACE, "CParticipant::get_MediaType:%x - exit S_OK", *plMediaTypes));

    return S_OK;
}


STDMETHODIMP CParticipant::put_Status(
    IN  ITStream *      pITStream,
    IN  VARIANT_BOOL    fEnable
    )
{
/* this is a new feature, we still can not really control the participant.
    LOG((MSP_TRACE, "CParticipant::put_Status, pITStream %p, status %hs",
        pITStream, fEnable ? "Enable" : "Disable"
        ));

    HRESULT hr;

    // if the caller specified a stream, find the stream and use it.
    if (pITStream != NULL)
    {
        m_lock.Lock();

        int index;
        if ((index = m_Streams.Find(pITStream)) < 0)
        {
            m_lock.Unlock();
            
            LOG((MSP_ERROR, "CParticipant::put_Status, stream %p not found",
                pITStream,));

            return E_INVALIDARG;
        }
        
        // add ref so that it won't go away.
        pITStream->AddRef();

        m_lock.Unlock;

        hr = ((CIPConfMSPStream *)pITStream)->EnableParticipant(
            fEnable
            );

        pITStream->Release();

        return hr;
    }

    // if the caller didn't specify a stream, set the status on all streams.
    m_lock.Lock();
    int nSize = m_Streams.GetSize();
    ITStream ** Streams = (ITSTream **)malloc(sizeof(ITStream*) * nSize);

    if (Streams == NULL)
    {
        m_lock.Unlock();
        LOG((MSP_ERROR, "CParticipant::put_Status out of memory"));
        return E_OUTOFMEMORY;
    }

    for (int i = 0; i < nSize; i ++)
    {
        Streams[i] = m_Streams[i];
        Streams[i]->AddRef();
    }
    m_lock.Unlock();

    for (i = 0; i < nSize; i ++)
    {
        hr = Streams[i]->EnableParticipant(fEnable);

        if (FAILED(hr))
        {
            break;
        }
    }

    for (i = 0; i < nSize; i ++)
    {
        Streams[i]->Release();
    }
    free(Streams);

    return hr;
*/
    return E_NOTIMPL;
}

STDMETHODIMP CParticipant::get_Status(
    IN  ITStream *      pITStream,
    OUT VARIANT_BOOL *  pStatus
    )
{
    return E_NOTIMPL;
}

STDMETHODIMP CParticipant::EnumerateStreams(
    OUT     IEnumStream **      ppEnumStream
    )
{
    LOG((MSP_TRACE, 
        "EnumerateStreams entered. ppEnumStream:%x", ppEnumStream));

    //
    // Check parameters.
    //

    if (IsBadWritePtr(ppEnumStream, sizeof(VOID *)))
    {
        LOG((MSP_ERROR, "CMSPCallBase::EnumerateStreams - "
            "bad pointer argument - exit E_POINTER"));

        return E_POINTER;
    }

    //
    // First see if this call has been shut down.
    // acquire the lock before accessing the stream object list.
    //

    CLock lock(m_lock);

    if (m_Streams.GetData() == NULL)
    {
        LOG((MSP_ERROR, "CMSPCallBase::EnumerateStreams - "
            "call appears to have been shut down - exit E_UNEXPECTED"));

        // This call has been shut down.
        return E_UNEXPECTED;
    }

    //
    // Create an enumerator object.
    //

    typedef _CopyInterface<ITStream> CCopy;
    typedef CSafeComEnum<IEnumStream, &IID_IEnumStream, 
                ITStream *, CCopy> CEnumerator;

    HRESULT hr;

    CComObject<CEnumerator> *pEnum = NULL;

    hr = CComObject<CEnumerator>::CreateInstance(&pEnum);
    if (pEnum == NULL)
    {
        LOG((MSP_ERROR, "CMSPCallBase::EnumerateStreams - "
            "Could not create enumerator object, %x", hr));

        return hr;
    }

    //
    // query for the IID_IEnumStream i/f
    //

    hr = pEnum->_InternalQueryInterface(IID_IEnumStream, (void**)ppEnumStream);
    
    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "CMSPCallBase::EnumerateStreams - "
            "query enum interface failed, %x", hr));

        delete pEnum;
        return hr;
    }

    //
    // Init the enumerator object. The CSafeComEnum can handle zero-sized array.
    //

    hr = pEnum->Init(
        m_Streams.GetData(),                        // the begin itor
        m_Streams.GetData() + m_Streams.GetSize(),  // the end itor, 
        NULL,                                       // IUnknown
        AtlFlagCopy                                 // copy the data.
        );

    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "CMSPCallBase::EnumerateStreams - "
            "init enumerator object failed, %x", hr));

        (*ppEnumStream)->Release();
        return hr;
    }

    LOG((MSP_TRACE, "CMSPCallBase::EnumerateStreams - exit S_OK"));

    return hr;
}

STDMETHODIMP CParticipant::get_Streams(
    OUT     VARIANT *           pVariant
    )
{
    LOG((MSP_TRACE, "CParticipant::get_Streams - enter"));

    //
    // Check parameters.
    //

    if ( IsBadWritePtr(pVariant, sizeof(VARIANT) ) )
    {
        LOG((MSP_ERROR, "CParticipant::get_Streams - "
            "bad pointer argument - exit E_POINTER"));

        return E_POINTER;
    }

    //
    // See if this call has been shut down. Acquire the lock before accessing
    // the stream object list.
    //

    CLock lock(m_lock);

    if (m_Streams.GetData() == NULL)
    {
        LOG((MSP_ERROR, "CParticipant::get_Streams - "
            "call appears to have been shut down - exit E_UNEXPECTED"));

        // This call has been shut down.
        return E_UNEXPECTED;
    }

    //
    // create the collection object - see mspcoll.h
    //

    typedef CTapiIfCollection< ITStream * > StreamCollection;
    CComObject<StreamCollection> * pCollection;
    HRESULT hr = CComObject<StreamCollection>::CreateInstance( &pCollection );

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CParticipant::get_Streams - "
            "can't create collection - exit 0x%08x", hr));

        return hr;
    }

    //
    // get the Collection's IDispatch interface
    //

    IDispatch * pDispatch;

    hr = pCollection->_InternalQueryInterface(IID_IDispatch,
                                              (void **) &pDispatch );

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CParticipant::get_Streams - "
            "QI for IDispatch on collection failed - exit 0x%08x", hr));

        delete pCollection;

        return hr;
    }

    //
    // Init the collection using an iterator -- pointers to the beginning and
    // the ending element plus one.
    //

    hr = pCollection->Initialize( m_Streams.GetSize(),
                                  m_Streams.GetData(),
                                  m_Streams.GetData() + m_Streams.GetSize() );

    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "CParticipant::get_Streams - "
            "Initialize on collection failed - exit 0x%08x", hr));
        
        pDispatch->Release();
        return hr;
    }

    //
    // put the IDispatch interface pointer into the variant
    //

    LOG((MSP_INFO, "CParticipant::get_Streams - "
        "placing IDispatch value %08x in variant", pDispatch));

    VariantInit(pVariant);
    pVariant->vt = VT_DISPATCH;
    pVariant->pdispVal = pDispatch;

    LOG((MSP_TRACE, "CParticipant::get_Streams - exit S_OK"));
    return S_OK;
}

HRESULT CParticipant::AddStream(
    IN  ITStream *      pITStream, 
    IN  DWORD           dwSSRC,
    IN  DWORD           dwSendRecv,
    IN  DWORD           dwMediaType
    )
/*++

Routine Description:

    A participant might appear on more than one streams. This function adds
    a new stream and the SSRC into the participant's list.

Arguments:
    
    pITStream - the stream that has the participant.

    dwSSRC - the SSRC of the participant in that stream.

    dwSendRecv - the participant is a sender or receiver in the stream.

    dwMediaType - the media type of the stream.

Return Value:

    S_OK,
    E_OUTOFMEMORY,
*/
{
    CLock lock(m_lock);

    // if the stream is already there, update the SSRC and return.
    int index = m_Streams.Find(pITStream);
    if ( index >= 0)
    {
        m_StreamInfo[index].dwSSRC = dwSSRC;
        m_StreamInfo[index].dwSendRecv |= dwSendRecv;
        return S_OK;
    }

    // add the stream.
    if (!m_Streams.Add(pITStream))
    {
        return E_OUTOFMEMORY;
    }
    
    // add the SSRC and sender flag.
    STREAM_INFO Info;
    Info.dwSSRC = dwSSRC;
    Info.dwSendRecv = dwSendRecv;

    if (!m_StreamInfo.Add(Info))
    {
        m_Streams.Remove(pITStream);

        return E_OUTOFMEMORY;
    }

    pITStream->AddRef();

    // update the mediatype.
    if (dwSendRecv & PART_SEND)
    {
        m_dwSendingMediaTypes |= dwMediaType;
    }
    if (dwSendRecv & PART_RECV)
    {
        m_dwReceivingMediaTypes |= dwMediaType;
    }

    return S_OK;
}

HRESULT CParticipant::RemoveStream(
    IN  ITStream *  pITStream,
    IN  DWORD       dwSSRC,
    OUT BOOL *      pbLast
    )
/*++

Routine Description:

    A participant might appear on more than one streams. This function remove
    a stream from the participant's list.

Arguments:
    
    pITStream - the stream that has the participant.

    dwSSRC - the SSRC of the participant in that stream.

    pbLast - the memory space to store a boolean value, specifying if the 
             stream removed was the last one in the list.

Return Value:

    S_OK,
    E_POINTER,
*/
{
    CLock lock(m_lock);
    
    // first find the stream.
    int index = m_Streams.Find(pITStream);

    if (index < 0)
    {
        return E_FAIL;
    }
    
    if (m_Streams.GetSize()  != m_StreamInfo.GetSize())
    {
        return E_UNEXPECTED;
    }

    // then check the SSRC.
    if (m_StreamInfo[index].dwSSRC != dwSSRC)
    {
        // this is not the participant being looking for.
        return E_FAIL;
    }

    // SSRC match, we found the participant. remove the stream and info.
    m_Streams.RemoveAt(index);
    m_StreamInfo.RemoveAt(index);

    // release the refcount we had in the list.
    pITStream->Release();

    // recalculate the media types.
    m_dwSendingMediaTypes = 0;
    m_dwReceivingMediaTypes = 0;
    
    for (int i = 0; i < m_Streams.GetSize(); i ++)
    {
        if (m_StreamInfo[i].dwSendRecv & PART_SEND)
        {
            m_dwSendingMediaTypes |= ((CIPConfMSPStream *)m_Streams[i])->MediaType();
        }

        if (m_StreamInfo[i].dwSendRecv & PART_RECV)
        {
            m_dwReceivingMediaTypes |= ((CIPConfMSPStream *)m_Streams[i])->MediaType();
        }
    }

    *pbLast = (m_Streams.GetSize() == 0);

    return S_OK;
}

BOOL CParticipantList::FindByCName(char *szCName, int *pIndex) const
/*++

Routine Description:

    Find a participant by its canonical name. If the function returns true,
    *pIndex contains the index of the participant. If the function returns
    false, *pIndex contains the index where the new participant should be
    inserted.

Arguments:
    
    szCName - the canonical name of the participant.

    pIndex - the memory address to store an integer.

Return Value:

    TRUE - the participant is found.

    FALSE - the participant is not in the list.
*/
{
    for(int i = 0; i < m_nSize; i++)
    {
        // This list is an ordered list based on dictionary order. We are using
        // a linear search here, it could be changed to a binary search.

        // CompareCName will return 0 if the name is the same, <0 if the szCName
        // is bigger, >0 if the szCName is smaller.
        int res = ((CParticipant *)m_aT[i])->CompareCName(szCName);
        if(res >= 0) 
        {
            *pIndex = i;
            return (res == 0);
        }
    }
    *pIndex = m_nSize;
    return FALSE;   // not found
}

BOOL CParticipantList::InsertAt(int nIndex, ITParticipant *pITParticipant)
/*++

Routine Description:

    Insert a participant into the list at a given index.

Arguments:
    
    nIndex - the location where the new object is inserted.

    pITParticipant - the object to be inserted.

Return Value:

    TRUE - the participant is inserted.

    FALSE - out of memory.
*/
{
    _ASSERTE(nIndex >= 0 && nIndex <= m_nSize);
    if(m_nSize == m_nAllocSize)
    {
        if (!Grow()) return FALSE;
    }

    memmove((void*)&m_aT[nIndex+1], (void*)&m_aT[nIndex], 
        (m_nSize - nIndex) * sizeof(ITParticipant *));

    m_nSize++;

    SetAtIndex(nIndex, pITParticipant);

    return TRUE;
}

CParticipantEvent::CParticipantEvent()
    : m_pFTM(NULL),
      m_pITParticipant(NULL),
      m_pITSubStream(NULL),
      m_Event(PE_NEW_PARTICIPANT)
{}

// methods called by the call object.
HRESULT CParticipantEvent::Init(
    IN  PARTICIPANT_EVENT   Event,
    IN  ITParticipant *     pITParticipant,
    IN  ITSubStream *       pITSubStream
    )
/*++

Routine Description:

    Initialize the ParticipantEvent object.

Arguments:
    
    Event - the event.

    pITParticipant - the participant.

    pITSubStream - the substream, can be NULL.

Return Value:

    S_OK,

--*/
{
    LOG((MSP_TRACE, "CParticipantEvent::Init"));

    // create the marshaler.
    HRESULT hr;
    hr = CoCreateFreeThreadedMarshaler(GetControllingUnknown(), &m_pFTM);
    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "create marshaler failed, %x", hr));
        return hr;
    }

    m_Event             = Event;
    
    m_pITParticipant    = pITParticipant;
    if (m_pITParticipant) m_pITParticipant->AddRef();

    m_pITSubStream      = pITSubStream;
    if (m_pITSubStream) m_pITSubStream->AddRef();

    LOG((MSP_TRACE, "CParticipantEvent Init returns S_OK"));
    return S_OK;
}

void CParticipantEvent::FinalRelease()
/*++

Routine Description:

    release everything before being deleted. 

Arguments:
    
Return Value:

--*/
{
    LOG((MSP_TRACE, "CParticipantEvent::FinalRelease - enter"));

    if (m_pFTM)
    {
        m_pFTM->Release();
    }
    
    if (m_pITParticipant) m_pITParticipant->Release();

    if (m_pITSubStream) m_pITSubStream->Release();

    LOG((MSP_TRACE, "CParticipantEvent::FinalRelease - exit"));
}

STDMETHODIMP CParticipantEvent::get_Event(
    OUT PARTICIPANT_EVENT * pParticipantEvent
    )
{
    if (IsBadWritePtr(pParticipantEvent, sizeof (PARTICIPANT_EVENT)))
    {
        LOG((MSP_ERROR, "CParticipantEvent::get_Event - exit E_POINTER"));

        return E_POINTER;
    }

    *pParticipantEvent = m_Event;

    return S_OK;
}

STDMETHODIMP CParticipantEvent::get_Participant(
    OUT ITParticipant ** ppITParticipant
    )
{
    if (IsBadWritePtr(ppITParticipant, sizeof (void *)))
    {
        LOG((MSP_ERROR, "CParticipantEvent::get_participant - exit E_POINTER"));

        return E_POINTER;
    }

    if (!m_pITParticipant)
    {
        LOG((MSP_ERROR, "CParticipantevnt::get_Participant - exit no item"));
        return TAPI_E_NOITEMS;
    }

    m_pITParticipant->AddRef();
    *ppITParticipant = m_pITParticipant;

    return S_OK;
}

STDMETHODIMP CParticipantEvent::get_SubStream(
    OUT ITSubStream** ppSubStream
    )
{
    if (IsBadWritePtr(ppSubStream, sizeof (void *)))
    {
        LOG((MSP_ERROR, "CParticipantEvent::get_SubStream - exit E_POINTER"));

        return E_POINTER;
    }

    if (!m_pITSubStream)
    {
        LOG((MSP_WARN, "CParticipantevnt::get_SubStream - exit no item"));
        return TAPI_E_NOITEMS;
    }

    m_pITSubStream->AddRef();
    *ppSubStream = m_pITSubStream;

    return S_OK;
}

HRESULT CreateParticipantEvent(
    IN  PARTICIPANT_EVENT       Event,
    IN  ITParticipant *         pITParticipant,
    IN  ITSubStream *           pITSubStream,
    OUT IDispatch **            ppIDispatch
    )
{
    // create the object.
    CComObject<CParticipantEvent> * pCOMParticipantEvent;

    HRESULT hr = CComObject<CParticipantEvent>
                    ::CreateInstance(&pCOMParticipantEvent);

    if (NULL == pCOMParticipantEvent)
    {
        LOG((MSP_ERROR, "could not create participant event:%x", hr));
        return hr;
    }

    IDispatch * pIDispatch;

    // get the interface pointer.
    hr = pCOMParticipantEvent->_InternalQueryInterface(
        IID_IDispatch, 
        (void **)&pIDispatch
        );

    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "Create ParticipantEvent QueryInterface failed: %x", hr));
        delete pCOMParticipantEvent;
        return hr;
    }

    // Initialize the object.
    hr = pCOMParticipantEvent->Init(
        Event,
        pITParticipant,
        pITSubStream
        );

    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "CreateMSPParticipantEvent:call init failed: %x", hr));
        pIDispatch->Release();

        return hr;
    }

    *ppIDispatch = pIDispatch;
    
    return S_OK;
}

HRESULT CreateParticipantEnumerator(
    IN  ITParticipant **    begin,
    IN  ITParticipant **    end,
    OUT IEnumParticipant ** ppEnumParticipant
    )
{
    //
    // Create an enumerator object.
    //

    typedef _CopyInterface<ITParticipant> CCopy;
    typedef CSafeComEnum<IEnumParticipant, &IID_IEnumParticipant, 
                ITParticipant *, CCopy> CEnumerator;

    HRESULT hr;

    CComObject<CEnumerator> *pEnum = NULL;

    hr = CComObject<CEnumerator>::CreateInstance(&pEnum);
    if (pEnum == NULL)
    {
        LOG((MSP_ERROR, "CreateParticipantEnumerator - "
            "Could not create enumerator object, %x", hr));

        return hr;
    }

    //
    // query for the IID_IEnumParticipant i/f
    //

    hr = pEnum->_InternalQueryInterface(
        IID_IEnumParticipant, 
        (void**)ppEnumParticipant
        );
    
    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "CreateParticipantEnumerator - "
            "query enum interface failed, %x", hr));

        delete pEnum;
        return hr;
    }

    //
    // Init the enumerator object. The CSafeComEnum can handle zero-sized array.
    //

    hr = pEnum->Init(
        begin,                        // the begin itor
        end,  // the end itor, 
        NULL,                                       // IUnknown
        AtlFlagCopy                                 // copy the data.
        );

    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "CreateParticipantEnumerator - "
            "init enumerator object failed, %x", hr));

        (*ppEnumParticipant)->Release();
        return hr;
    }

    LOG((MSP_TRACE, "CreateParticipantEnumerator - exit S_OK"));

    return hr;
}

HRESULT CreateParticipantCollection(
    IN  ITParticipant **    begin,
    IN  ITParticipant **    end,
    IN  int                 nSize,
    OUT VARIANT *           pVariant
    )
{
    //
    // create the collection object - see mspcoll.h
    //

    typedef CTapiIfCollection< ITParticipant * > ParticipantCollection;
    CComObject<ParticipantCollection> * pCollection;
    HRESULT hr = CComObject<ParticipantCollection>::CreateInstance( &pCollection );

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CreateParticipantCollection - "
            "can't create collection - exit 0x%08x", hr));

        return hr;
    }

    //
    // get the Collection's IDispatch interface
    //

    IDispatch * pDispatch;

    hr = pCollection->_InternalQueryInterface(IID_IDispatch,
                                              (void **) &pDispatch );

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CreateParticipantCollection - "
            "QI for IDispatch on collection failed - exit 0x%08x", hr));

        delete pCollection;

        return hr;
    }

    //
    // Init the collection using an iterator -- pointers to the beginning and
    // the ending element plus one.
    //

    hr = pCollection->Initialize(nSize, begin, end);

    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "CreateParticipantCollection- "
            "Initialize on collection failed - exit 0x%08x", hr));
        
        pDispatch->Release();
        return hr;
    }

    //
    // put the IDispatch interface pointer into the variant
    //

    VariantInit(pVariant);
    pVariant->vt = VT_DISPATCH;
    pVariant->pdispVal = pDispatch;

    LOG((MSP_TRACE, "CreateParticipantCollection - exit S_OK"));
 
    return S_OK;
}

