/////////////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1997 Active Voice Corporation. All Rights Reserved. 
//
// Active Agent(r) and Unified Communications(tm) are trademarks of Active Voice Corporation.
//
// Other brand and product names used herein are trademarks of their respective owners.
//
// The entire program and user interface including the structure, sequence, selection, 
// and arrangement of the dialog, the exclusively "yes" and "no" choices represented 
// by "1" and "2," and each dialog message are protected by copyrights registered in 
// the United States and by international treaties.
//
// Protected by one or more of the following United States patents: 5,070,526, 5,488,650, 
// 5,434,906, 5,581,604, 5,533,102, 5,568,540, 5,625,676, 5,651,054.
//
// Active Voice Corporation
// Seattle, Washington
// USA
//
/////////////////////////////////////////////////////////////////////////////////////////

// VideoFeed.cpp : Implementation of CVideoFeed
#include "stdafx.h"
#include "TapiDialer.h"
#include "ConfRoom.h"
#include "VideoFeed.h"
#include "Particip.h"
#include "strmif.h"


// Assorted helper functions

void GetParticipantInfoHelper( ITParticipant *pParticipant, PARTICIPANT_TYPED_INFO nType, CComBSTR &bstrInfo )
{
    BSTR bstrTemp = NULL;
    pParticipant->get_ParticipantTypedInfo( nType, &bstrTemp );
    if ( bstrTemp && SysStringLen(bstrTemp) )
    {
        if ( bstrInfo.Length() )
            bstrInfo.Append( L"\n" );

        bstrInfo.Append( bstrTemp );
    }

    SysFreeString( bstrTemp );
}


void GetParticipantInfo( ITParticipant *pParticipant, BSTR *pbstrInfo )
{
    //_ASSERT( pParticipant && pbstrInfo );
    //
    // We have to verify the pbstrInfo is a valid pointer
    //

    if( NULL == pbstrInfo )
    {
        return;
    }

    CComBSTR bstrInfo;
    GetParticipantInfoHelper( pParticipant, PTI_CANONICALNAME, bstrInfo );
    GetParticipantInfoHelper( pParticipant, PTI_EMAILADDRESS, bstrInfo );
    GetParticipantInfoHelper( pParticipant, PTI_PHONENUMBER, bstrInfo );
    GetParticipantInfoHelper( pParticipant, PTI_LOCATION, bstrInfo );
    GetParticipantInfoHelper( pParticipant, PTI_TOOL, bstrInfo );
    GetParticipantInfoHelper( pParticipant, PTI_NOTES, bstrInfo );

    *pbstrInfo = SysAllocString( bstrInfo );
}


/////////////////////////////////////////////////////////////////////////////
// CVideoFeed

CVideoFeed::CVideoFeed()
{
    SetRectEmpty(&m_rc);
    m_pVideo = NULL;
    m_bPreview = false;
    m_bRequestQOS = false;
    m_bstrName = NULL;
    m_pITParticipant = NULL;

    // Default name for video
    GetNameFromVideo( NULL, &m_bstrName, NULL, false, false );
}

void CVideoFeed::FinalRelease()
{
    ATLTRACE(_T(".enter.CVideoFeed::FinalRelease().\n") );
    SysFreeString( m_bstrName );

#ifdef _DEBUG
    if ( m_pVideo )
    {
        m_pVideo->AddRef();
        ATLTRACE(_T("\tm_pVideo ref count @ %ld.\n"), m_pVideo->Release() );
    }

    if ( m_pITParticipant )
    {
        m_pITParticipant->AddRef();
        ATLTRACE(_T("\tm_pITParticipant ref count @ %ld.\n"), m_pITParticipant->Release() );
    }
#endif

    RELEASE( m_pVideo );
    RELEASE( m_pITParticipant );
}

STDMETHODIMP CVideoFeed::get_bstrName(BSTR *ppVal)
{
    HRESULT hr;

    Lock();
    if ( m_bPreview )
        hr = GetNameFromVideo( NULL, ppVal, NULL, false, true );
    else
        hr = SysReAllocString( ppVal, m_bstrName );
    Unlock();

    return hr;
}

STDMETHODIMP CVideoFeed::UpdateName()
{
    Lock();
    // Clean up first 
    SysFreeString( m_bstrName );
    m_bstrName = NULL;

    // Retrieve appropriate information
    HRESULT hr = GetNameFromVideo( m_pVideo, &m_bstrName, NULL, false, (bool) (m_bPreview != 0) );
    Unlock();

    return hr;
}


STDMETHODIMP CVideoFeed::get_IVideoWindow(IUnknown **ppVal)
{
    HRESULT hr = E_FAIL;
    Lock();
    if ( m_pVideo )
    {
        hr = m_pVideo->QueryInterface( IID_IVideoWindow, (void **) ppVal );
    }
    Unlock();

    return hr;
}

STDMETHODIMP CVideoFeed::put_IVideoWindow(IUnknown * newVal)
{
    HRESULT hr = S_OK;

    Lock();
    RELEASE( m_pVideo );
    if ( newVal )
        hr = newVal->QueryInterface( IID_IVideoWindow, (void **) &m_pVideo );
    Unlock();

    return hr;
}

STDMETHODIMP CVideoFeed::Paint(ULONG_PTR hDC, HWND hWndSource)
{
    Lock();
    _ASSERT( m_pVideo && hDC );

    // Verify we have a video feed
    if ( !m_pVideo )
    {
        Unlock();
        return E_PENDING;
    }
    Unlock();

    // Verify that the window and DC are ok
    if ( !hDC || !IsWindow((HWND) hWndSource) )    return E_INVALIDARG;

    HRESULT hr;
    IDrawVideoImage *pDraw;
    if ( SUCCEEDED(hr = m_pVideo->QueryInterface(IID_IDrawVideoImage, (void **) &pDraw)) )
    {
        RECT rc;
        get_rc( &rc );

        SetStretchBltMode((HDC) hDC, COLORONCOLOR);
        pDraw->DrawVideoImageDraw( (HDC) hDC, NULL, &rc );
        pDraw->Release();

        // Draw border around feed
        InflateRect( &rc, SEL_DX, SEL_DY );
        rc.right++;
        Draw3dBox( (HDC) hDC, rc, false );
    }

    return hr;
}

HRESULT CVideoFeed::GetNameFromParticipant(ITParticipant *pParticipant, BSTR * pbstrName, BSTR *pbstrInfo )
{
    _ASSERT( pbstrName );
    *pbstrName = NULL;
    if ( pbstrInfo ) *pbstrInfo = NULL;

    // Grab name from participant info
    if ( pParticipant )
    {
        pParticipant->get_ParticipantTypedInfo( PTI_NAME, pbstrName );

        // Fetch all other participant information
        if ( pbstrInfo )
            GetParticipantInfo( pParticipant, pbstrInfo );
    }

    return S_OK;
}


STDMETHODIMP CVideoFeed::GetNameFromVideo(IUnknown * pVideo, BSTR * pbstrName, BSTR * pbstrInfo, VARIANT_BOOL bAllowNull, VARIANT_BOOL bPreview)
{
    _ASSERT( pbstrName );

    UINT nIDSParticipant = IDS_PARTICIPANT;
    *pbstrName = NULL;
    if ( pbstrInfo ) *pbstrInfo = NULL;

    // Grab name from participant info
    if ( !bPreview && pVideo )
    {
        nIDSParticipant = IDS_NO_PARTICIPANT;

        ITTerminal *pITTerminal;
        if ( SUCCEEDED(pVideo->QueryInterface(IID_ITTerminal, (void **) &pITTerminal)) )
        {
            // Is this terminal showing preview video?
            TERMINAL_DIRECTION nDir;
            pITTerminal->get_Direction( &nDir );
            if ( nDir == TD_CAPTURE )
            {
                bPreview = true;
            }
            else
            {
                bPreview = false;
                ITParticipant *pParticipant;
                if ( SUCCEEDED(get_ITParticipant(&pParticipant)) )
                {
                    GetNameFromParticipant( pParticipant, pbstrName, pbstrInfo );
                    pParticipant->Release();
                }
            }
            pITTerminal->Release();
        }
    }

    // Use stock name from resources
    if ( ((!bAllowNull || bPreview) && (*pbstrName == NULL)) || (*pbstrName && !SysStringLen(*pbstrName)) )
    {
        USES_CONVERSION;
        TCHAR szText[255];
        UINT nIDS = (bPreview) ? IDS_VIDEOPREVIEW : nIDSParticipant;

        LoadString( _Module.GetResourceInstance(), nIDS, szText, ARRAYSIZE(szText) );
        SysReAllocString( pbstrName, T2COLE(szText) );
    }

    return S_OK;
}


STDMETHODIMP CVideoFeed::get_rc(RECT * pVal)
{
    Lock();
    *pVal = m_rc;
    Unlock();
    return S_OK;
}

STDMETHODIMP CVideoFeed::put_rc(RECT newVal)
{
    Lock();
    m_rc = newVal;
    Unlock();
    return S_OK;
}

STDMETHODIMP CVideoFeed::put_ITParticipant(ITParticipant *newVal)
{
    HRESULT hr = S_OK;

    Lock();
    RELEASE( m_pITParticipant );
    if ( newVal )
        hr = newVal->QueryInterface( IID_ITParticipant, (void **) &m_pITParticipant );
    Unlock();

    return hr;
}

STDMETHODIMP CVideoFeed::get_ITParticipant(ITParticipant **ppVal)
{
    HRESULT hr = E_FAIL;
    Lock();
    if ( m_pITParticipant )
        hr = m_pITParticipant->QueryInterface( IID_ITParticipant, (void **) ppVal );
    Unlock();

    return hr;
}

STDMETHODIMP CVideoFeed::get_bPreview(VARIANT_BOOL * pVal)
{
    Lock();
    *pVal = m_bPreview;
    Unlock();

    return S_OK;
}

STDMETHODIMP CVideoFeed::put_bPreview(VARIANT_BOOL newVal)
{
    ATLTRACE(_T(".enter.CVideoFeed::put_bPreview(%p, %d).\n"), this, newVal );
    Lock();
    m_bPreview = newVal;
    Unlock();

    return S_OK;
}


STDMETHODIMP CVideoFeed::get_bRequestQOS(VARIANT_BOOL * pVal)
{
    Lock();
    *pVal = m_bRequestQOS;
    Unlock();

    return S_OK;
}

STDMETHODIMP CVideoFeed::put_bRequestQOS(VARIANT_BOOL newVal)
{
    Lock();
    m_bRequestQOS = newVal;
    Unlock();

    // Set the color of the video window's border to match the state of the video feed
    IVideoWindow *pVideo;
    if ( SUCCEEDED(get_IVideoWindow((IUnknown **) &pVideo)) )
    {
        pVideo->put_BorderColor( GetSysColor((newVal) ? COLOR_HIGHLIGHT : COLOR_WINDOWFRAME) );
        pVideo->Release();
    }

    return S_OK;
}

STDMETHODIMP CVideoFeed::IsVideoStreaming( VARIANT_BOOL bIncludePreview )
{
    Lock();
    HRESULT hr = ((bIncludePreview && m_bPreview) || (m_pITParticipant != NULL)) ? S_OK : S_FALSE;
    Unlock();

    return hr;
}


STDMETHODIMP CVideoFeed::get_ITSubStream(ITSubStream **ppVal)
{
    HRESULT hr = E_FAIL;

    ITParticipant *pParticipant;
    if ( SUCCEEDED(hr = get_ITParticipant(&pParticipant)) )
    {
        IEnumStream *pEnum;
        if ( SUCCEEDED(hr = pParticipant->EnumerateStreams(&pEnum)) )
        {
            ITStream *pStream;
            while ( (hr = pEnum->Next(1, &pStream, NULL)) == S_OK )
            {
                long nMediaType;
                TERMINAL_DIRECTION nDir;

                pStream->get_MediaType( &nMediaType );
                if ( nMediaType == TAPIMEDIATYPE_VIDEO )
                {
                    pStream->get_Direction( &nDir );
                    if ( nDir == TD_RENDER )
                    {
                        // This stream is going the right direction
                        ITParticipantSubStreamControl *pControl;
                        if ( SUCCEEDED(hr = pStream->QueryInterface(IID_ITParticipantSubStreamControl, (void **) &pControl)) )
                        {
                            hr = pControl->get_SubStreamFromParticipant( pParticipant, ppVal );
                            pControl->Release();
                        }
                    }
                }
                pStream->Release();
            }
            
            pEnum->Release();
        }
        pParticipant->Release();
    }

    return S_OK;
}

STDMETHODIMP CVideoFeed::MapToParticipant(ITParticipant * pNewParticipant)
{
    bool bContinue = true;
    HRESULT hr = E_FAIL;

    ITStream *pStream;
    if ( SUCCEEDED(StreamFromParticipant(pNewParticipant, TAPIMEDIATYPE_VIDEO, TD_RENDER, &pStream)) )
    {
        // This stream is going the right direction
        ITParticipantSubStreamControl *pControl;
        if ( SUCCEEDED(hr = pStream->QueryInterface(IID_ITParticipantSubStreamControl, (void **) &pControl)) )
        {
            ITSubStream *pSubStream;
            if ( SUCCEEDED(hr = pControl->get_SubStreamFromParticipant(pNewParticipant, &pSubStream)) )
            {
                IVideoWindow *pVideo;
                if ( SUCCEEDED(hr = get_IVideoWindow((IUnknown **) &pVideo)) )
                {
                    ITTerminal *pTerminal;
                    if ( SUCCEEDED(hr = pVideo->QueryInterface(IID_ITTerminal, (void **) &pTerminal)) )
                    {
                        hr = pControl->SwitchTerminalToSubStream( pTerminal, pSubStream );
                        if ( SUCCEEDED(hr) )
                            put_ITParticipant( pNewParticipant );

                        pTerminal->Release();
                    }
                    pVideo->Release();
                }

                bContinue = false;
                pSubStream->Release();
            }
            pControl->Release();
        }
        pStream->Release();
    }

    return hr;
}