/*++

Copyright (c) 1997 - 1999 Microsoft Corporation

Module Name:

    terminal.cpp

Abstract:

    Implementation of terminals object for TAPI 3.0, that are
    not handled by the terminal manager.

    currently there are these terminals:

    phone

        Audio in and Audio out terminal associated with a phone device.
        Each phone device can have up to 3 types of devices -
        handset, headset, and speakerphone.  So, a single phone device
        can have 6 associated terminals!
        
Author:

    mquinton - 4/17/97

Notes:

    optional-notes

Revision History:

--*/

#include "stdafx.h"

HRESULT
WaitForPhoneReply(
                  DWORD dwID
                 );

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// CTerminal is the base terminal object that all other terminals are
// derived from.  It implements the ITTerminal methods.
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//
// ITTerminal methods
//


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// get_Name
//      Alloc and copy the name.  The app is responsible for
//      freeing
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
STDMETHODIMP
CTerminal::get_Name( 
    BSTR * ppName
    )
{
    LOG((TL_TRACE, "get_Name - enter" ));

    if (TAPIIsBadWritePtr( ppName, sizeof (BSTR) ) )
    {
        LOG((TL_ERROR, "getName - bad pointer"));

        return E_POINTER;
    }

    Lock();
    
    *ppName = SysAllocString( m_pName );

    Unlock();

    if ( ( NULL == *ppName ) && ( NULL != m_pName ) )
    {
        LOG((TL_ERROR, "get_Name - SysAllocString Failed" ));
        return E_OUTOFMEMORY;
    }


    LOG((TL_TRACE, "get_Name - exit - return S_OK" ));
    
    return S_OK;
}


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// get_State
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
STDMETHODIMP
CTerminal::get_State( 
    TERMINAL_STATE * pTerminalState
    )
{
    LOG((TL_TRACE, "get_State - enter" ));


    if (TAPIIsBadWritePtr( pTerminalState, sizeof(TERMINAL_STATE) ) )
    {
        LOG((TL_ERROR, "get_State - bad pointer"));

        return E_POINTER;
    }

    Lock();
    
    *pTerminalState = m_State;

    Unlock();

    LOG((TL_TRACE, "get_State - exit - return SUCCESS" ));
    
    return S_OK;
}


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// get_TerminalType
//          By definition, TAPI terminals are static
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
STDMETHODIMP
CTerminal::get_TerminalType( 
    TERMINAL_TYPE * pType
    )
{
    LOG((TL_TRACE, "get_TerminalType - enter" ));

    if ( TAPIIsBadWritePtr( pType, sizeof( TERMINAL_TYPE ) ) )
    {
        LOG((TL_ERROR, "get_TerminalType - bad pointer"));

        return E_POINTER;
    }
        

    Lock();
    
    *pType = m_TerminalType;

    Unlock();
    
    LOG((TL_TRACE, "get_TerminalType - exit - return S_OK" ));
    
    return S_OK;
}


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// get_TerminalClass
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
STDMETHODIMP
CTerminal::get_TerminalClass( 
    BSTR * pTerminalClass
    )
{
    HRESULT         hr = S_OK;
    CLSID           clsid;
    LPWSTR pClass = NULL;

    LOG((TL_TRACE, "get_TerminalClass - enter" ));

    if ( TAPIIsBadWritePtr( pTerminalClass, sizeof( BSTR ) ) )
    {
        LOG((TL_ERROR, "get_TerminalClass - bad pointer"));

        return E_POINTER;
    }

    Lock();
    
    hr = StringFromIID(
                       m_Class,
                       &pClass
                      );

    Unlock();
    
    if (S_OK != hr)
    {
        return hr;
    }

    *pTerminalClass = SysAllocString( pClass );

    if ( ( NULL == *pTerminalClass ) && ( NULL != pClass ) )
    {
        LOG((TL_ERROR, "get_TerminalClass - SysAllocString Failed" ));
        return E_OUTOFMEMORY;
    }


    CoTaskMemFree( pClass );

    LOG((TL_TRACE, "get_TerminalClass - exit - return SUCCESS" ));
    
    return S_OK;
}


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// get_MediaType
//
// returns the supported mediatype BSTR associated with this terminal.
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
STDMETHODIMP
CTerminal::get_MediaType(
                         long * plMediaType
                        )
{
    if ( TAPIIsBadWritePtr( plMediaType, sizeof(long) ) )
    {
        LOG((TL_ERROR, "get_MediaType - inval pointer"));

        return E_POINTER;
    }

    *plMediaType = m_lMediaType;

    return S_OK;
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// get_Direction
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
STDMETHODIMP
CTerminal::get_Direction(
                         TERMINAL_DIRECTION * pTerminalDirection
                        )
{
    if (TAPIIsBadWritePtr( pTerminalDirection, sizeof( TERMINAL_DIRECTION ) ) )
    {
        LOG((TL_ERROR, "get_Direction - bad pointer"));

        return E_POINTER;
    }

    Lock();
    
    *pTerminalDirection = m_Direction;

    Unlock();
    
    return S_OK;
    
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//  GetTerminalNameFromPhoneCaps
//
//  Creates a name for a phone device terminal based on the phone
//  caps
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
PWSTR
GetTerminalNameFromPhoneCaps(
                             LPPHONECAPS pPhoneCaps,
                             DWORD dwHookSwitchDev,
                             DWORD dwPhoneID
                            )
{
    PWSTR       pszName;
    PWSTR       pszHookSwitchDevName = NULL;

    
    //
    // load the appropriate string to describe
    // the hookswitch device
    //
    switch (dwHookSwitchDev)
    {
        case PHONEHOOKSWITCHDEV_SPEAKER:
        {
            pszHookSwitchDevName = MyLoadString( IDS_SPEAKERTERMINAL );

            break;
        }
        case PHONEHOOKSWITCHDEV_HANDSET:
        {
            pszHookSwitchDevName = MyLoadString( IDS_HANDSETTERMINAL );

            break;
        }
        case PHONEHOOKSWITCHDEV_HEADSET:
        {
            pszHookSwitchDevName = MyLoadString( IDS_HEADSETTERMINAL );

            break;
        }
        default:
        {
            return NULL;
        }
    }

    if ( NULL == pszHookSwitchDevName )
    {
        return NULL;
    }
    
    //
    // if the sp supplies a name, use it
    //
    if ( 0 != pPhoneCaps->dwPhoneNameSize )
    {
        pszName = (PWSTR) ClientAlloc(
                                      ( pPhoneCaps->dwPhoneNameSize +
                                        lstrlenW( pszHookSwitchDevName ) +
                                        32 ) * sizeof(WCHAR)
                                     );

        if (NULL == pszName)
        {
            ClientFree( pszHookSwitchDevName );
            
            return NULL;
        }

        CopyMemory(
                   pszName,
                   ( ( (LPBYTE)pPhoneCaps ) + pPhoneCaps->dwPhoneNameOffset ),
                   pPhoneCaps->dwPhoneNameSize
                  );

    }
    else
    {
        PWSTR               pszTempName;
        
        //
        // else create a name
        //
        pszTempName = MyLoadString( IDS_PHONEDEVICE );

        if ( NULL == pszTempName )
        {
            ClientFree( pszHookSwitchDevName );

            return NULL;
        }

        pszName =  (PWSTR) ClientAlloc(
                                       (lstrlenW( pszTempName ) +
                                        lstrlenW( pszHookSwitchDevName ) +
                                        32 ) * sizeof(WCHAR)
                                      );


        if (NULL == pszName)
        {
            ClientFree( pszHookSwitchDevName );
            ClientFree( pszTempName );
            
            return NULL;
        }

        wsprintfW(
                  pszName,
                  L"%s %d",
                  pszTempName,
                  dwPhoneID
                 );
        
        ClientFree( pszTempName );
    }

    //
    // put them together
    //
    lstrcatW(
             pszName,
             L" - "
            );

    lstrcatW(
             pszName,
             pszHookSwitchDevName
            );

    ClientFree( pszHookSwitchDevName );
    
    return pszName;
}
    


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//
// CTerminal::Create
//
// Static function to create an audio in terminal related to a phone
// device.
//
//      pAddress
//          Owning address
//
//      dwPhoneID
//          tapi 2 phone device ID related to this terminal
//
//      pPhoneCaps
//          PHONEDEVCAPS of the phone device
//
//      dwHookSwitchDev
//          PHONEHOOKSWITCHDEV_ bit of this phone device
//
//      ppTerminal
//          return the terminal here!
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
HRESULT
CTerminal::Create(
                  HPHONEAPP hPhoneApp,
                  DWORD dwPhoneID,
                  LPPHONECAPS pPhoneCaps,
                  DWORD dwHookSwitchDev,
                  TERMINAL_DIRECTION td,
                  DWORD dwAPIVersion,
                  ITTerminal ** ppTerminal
                 )
{
    HRESULT             hr = S_OK;

    
    CComObject< CTerminal > * p;
    
    //
    // create CTerminal
    //
    hr = CComObject< CTerminal >::CreateInstance( &p );


    if (!SUCCEEDED(hr))
    {
        LOG((TL_ERROR, "CreateInstance for Phone failed - %lx", hr));

        return hr;
    }

    //
    // save stuff
    //
    p->m_dwHookSwitchDev = dwHookSwitchDev;
    p->m_dwPhoneID = dwPhoneID;
    p->m_hPhoneApp = hPhoneApp;
    p->m_Direction = td;
    p->m_dwAPIVersion = dwAPIVersion;

    //
    // class depends on which hookswitchdev this is
    //
    switch( dwHookSwitchDev )
    {
        case PHONEHOOKSWITCHDEV_HANDSET:
            p->m_Class = CLSID_HandsetTerminal;
            break;
            
        case PHONEHOOKSWITCHDEV_HEADSET:
            p->m_Class = CLSID_HeadsetTerminal;
            break;
            
        case PHONEHOOKSWITCHDEV_SPEAKER:
            p->m_Class = CLSID_SpeakerphoneTerminal;
            break;

        default:
            break;
    }

    //
    // create a name
    //
    p->m_pName = GetTerminalNameFromPhoneCaps(
                                              pPhoneCaps,
                                              dwHookSwitchDev,
                                              dwPhoneID
                                             );

    //
    // return the created terminal
    //
    p->AddRef();
    *ppTerminal = p;

    #if DBG
        p->m_pDebug = (PWSTR) ClientAlloc( 1 );
    #endif

    return S_OK;
}


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
STDMETHODIMP
CTerminal::get_Gain(long *pVal)
{
    HRESULT                 hr;
    ITPhoneMSPCallPrivate * pMSPCall;

    LOG((TL_TRACE, "get_Gain - Enter"));
    
    if ( TAPIIsBadWritePtr( pVal, sizeof(LONG) ) )
    {
        LOG((TL_ERROR, "get_Gain - bad pointer"));

        return E_POINTER;
    }

    Lock();

    if ( NULL == m_pMSPCall )
    {
        Unlock();

        return TAPI_E_NOTERMINALSELECTED;
    }
    
    pMSPCall = m_pMSPCall;

    pMSPCall->AddRef();

    Unlock();
    
    hr = pMSPCall->GetGain( pVal, m_dwHookSwitchDev );

    pMSPCall->Release();
    
    LOG((TL_TRACE, "get_Gain - Exit - return %lx", hr));
    
    return hr;

}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
STDMETHODIMP
CTerminal::put_Gain(long newVal)
{
    HRESULT                 hr;
    ITPhoneMSPCallPrivate * pMSPCall;

    LOG((TL_TRACE, "put_Gain - Enter"));
    
    Lock();

    if ( NULL == m_pMSPCall )
    {
        Unlock();

        return TAPI_E_NOTERMINALSELECTED;
    }
    
    pMSPCall = m_pMSPCall;

    pMSPCall->AddRef();

    Unlock();
    
    hr = pMSPCall->PutGain( newVal, m_dwHookSwitchDev );

    pMSPCall->Release();

    LOG((TL_TRACE, "put_Gain - Exit - return %lx", hr));
    
    return hr;

    
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
STDMETHODIMP
CTerminal::get_Balance(long *pVal)
{
    LOG((TL_TRACE, "get_Balance - Enter"));
    
    LOG((TL_TRACE, "get_Balance - Exit - return TAPI_E_NOTSUPPORTED"));

    // not suppported
    return TAPI_E_NOTSUPPORTED;
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
STDMETHODIMP
CTerminal::put_Balance(long newVal)
{
    LOG((TL_TRACE, "put_Balance - Enter"));
    
    LOG((TL_TRACE, "put_Balance - Exit - return TAPI_E_NOTSUPPORTED"));

    // not supported
    return TAPI_E_NOTSUPPORTED;
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
STDMETHODIMP
CTerminal::get_Volume(long *pVal)
{
    HRESULT                 hr;
    ITPhoneMSPCallPrivate * pMSPCall;

    LOG((TL_TRACE, "get_Volume - Enter"));

    if ( TAPIIsBadWritePtr( pVal, sizeof( long ) ) )
    {
        LOG((TL_ERROR, "get_Volume - bad pointer"));

        return E_POINTER;
    }
    
    Lock();

    if ( NULL == m_pMSPCall )
    {
        Unlock();

        return TAPI_E_NOTERMINALSELECTED;
    }
    
    pMSPCall = m_pMSPCall;

    pMSPCall->AddRef();

    Unlock();
    
    hr = pMSPCall->GetVolume( pVal, m_dwHookSwitchDev );

    pMSPCall->Release();

    LOG((TL_TRACE, "get_Volume - Exit - return %lx", hr));
    
    return hr;
}


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
STDMETHODIMP
CTerminal::put_Volume(long newVal)
{
    HRESULT                 hr;
    ITPhoneMSPCallPrivate * pMSPCall;

    LOG((TL_TRACE, "put_Volume - Enter"));
    
    Lock();

    if ( NULL == m_pMSPCall )
    {
        Unlock();

        return TAPI_E_NOTERMINALSELECTED;
    }
    
    pMSPCall = m_pMSPCall;

    pMSPCall->AddRef();

    Unlock();
    
    hr = pMSPCall->PutVolume( newVal, m_dwHookSwitchDev );

    pMSPCall->Release();

    LOG((TL_TRACE, "put_Volume - Exit - return %lx", hr));

    return hr;
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
STDMETHODIMP
CTerminal::GetHookSwitchDev(DWORD * pdwHookSwitchDev)
{
    Lock();

    *pdwHookSwitchDev = m_dwHookSwitchDev;
    
    Unlock();

    return S_OK;
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
STDMETHODIMP
CTerminal::GetPhoneID(DWORD * pdwPhoneID)
{
    Lock();

    *pdwPhoneID = m_dwPhoneID;
    
    Unlock();

    return S_OK;
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
STDMETHODIMP
CTerminal::GetHPhoneApp(HPHONEAPP * phPhoneApp)
{
    Lock();

    *phPhoneApp = m_hPhoneApp;
    
    Unlock();

    return S_OK;
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
STDMETHODIMP
CTerminal::GetAPIVersion(DWORD * pdwAPIVersion)
{
    Lock();

    *pdwAPIVersion = m_dwAPIVersion;
    
    Unlock();

    return S_OK;
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
STDMETHODIMP
CTerminal::SetMSPCall(ITPhoneMSPCallPrivate * pPhoneMSPCall)
{
    Lock();

    m_pMSPCall = pPhoneMSPCall;

    if ( NULL != m_pMSPCall )
    {
        m_State = TS_INUSE;
    }
    else
    {
        m_State = TS_NOTINUSE;
    }

    Unlock();

    return S_OK;
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
void
CTerminal::FinalRelease()
{
    if (NULL != m_pName)
    {
        ClientFree( m_pName );
    }

#if DBG
    if( m_pDebug != NULL )
    {
        ClientFree( m_pDebug );
        m_pDebug = NULL;
    }
#endif


}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// CreateTerminalName
//
//      Creates a terminal name for 
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
PWSTR
CreateTerminalName(
                   CAddress * pAddress,
                   DWORD dwType,
                   long lMediaType
                  )
{
    PWSTR           pName;
    WCHAR           szTerminal[256];
    WCHAR           szMediaType[256];
    DWORD           dwSize;

    
    //
    // create the name
    //
    // first load the string "Terminal"
    //
    szTerminal[0] = L'\0';
    szMediaType[0] = L'\0';
    
    ::LoadStringW(_Module.GetResourceInstance(), IDS_TERMINAL, szTerminal, 256);

    dwSize = lstrlenW( szTerminal ) * sizeof (WCHAR);

    switch (dwType)
    {
        case 0:
            break;
        case 1:
            if ( LINEMEDIAMODE_DATAMODEM == lMediaType)
            {
                ::LoadStringW(_Module.GetResourceInstance(), IDS_DATAMODEM, szMediaType, 256);
            }
            else if ( LINEMEDIAMODE_G3FAX == lMediaType)
            {
                ::LoadStringW(_Module.GetResourceInstance(), IDS_G3FAX, szMediaType, 256);
            }

            dwSize += (lstrlenW( szMediaType ) * sizeof ( WCHAR ));
            
            break;
            
        default:
            break;
    }

    //
    // alloc enough memory for the name
    //
    dwSize += (lstrlenW( pAddress->GetAddressName() ) * sizeof(WCHAR));
    dwSize += 32;
    
    pName = (PWSTR) ClientAlloc( dwSize );

    if (NULL == pName)
    {
        return NULL;
    }


    //
    // terminal name should look like:
    // "<addressname> (mediatype) Terminal"
    //
    lstrcpyW(
             pName,
             pAddress->GetAddressName()
            );

    lstrcatW(
             pName,
             L" "
            );

    if (dwType == 1)
    {
        lstrcatW(
                 pName,
                 szMediaType
                );

        lstrcatW(
                 pName,
                 L" "
                );
    }
    
    lstrcatW(
             pName,
             szTerminal
            );

    return pName;
}