///////////////////////////////////////////////////////////////////////////////
//
//
//        Copyright (c) 1998-1999  Microsoft Corporation
//
//        Name: Manager.cpp
//
// Description: Implementation of the CTerminalManager object
//
///////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "termmgr.h"
#include "manager.h"
#include "PTUtil.h"

#define INSTANTIATE_GUIDS_NOW
#include "allterm.h"
#undef INSTANTIATE_GUIDS_NOW

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// CTerminalManager constructor
//
// Parameters:
//      None.
//
// Returns:
//      Nothing.
//


CTerminalManager::CTerminalManager()
{
    LOG((MSP_TRACE, "CTerminalManager::CTerminalManager - enter"));
    LOG((MSP_TRACE, "CTerminalManager::CTerminalManager - exit"));
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// GetDynamicTerminalClasses
//
// The MSP will call this method to get a list of dynamic terminal classes
// that the Terminal Manager supports.  The MSP is responsible for allocating
// and deallocating the ppTerminals buffer.
//
// Parameters:
//     dwMediaType:      [in] A DWORD bitmask consisting of one or more
//                           TAPIMEDIATYPEs ORed together. Most MSPs will
//                           pass in (DWORD) (TAPIMEDIATYPE_AUDIO |
//                           TAPIMEDIATYPE_VIDEO). If an MSP is only
//                           interested in terminal classes that can be used
//                           to create terminals with a particular media
//                           type, it may pass in that media type instead
//                           (e.g., TAPIMEDIATYPE_AUDIO).
//     pdwNumClasses:    [in, out] Pointer to a DWORD.  On entry, indicates
//                           the size of the buffer pointed to in
//                           pTerminalClasses. On success, it will be filled
//                           in with the actual number of class IIDs returned.
//                           If the buffer is not big enough, the method will
//                           return TAPI_E_NOTENOUGHMEMORY, and it will be
//                           filled in the with number of IIDs needed. 
//     pTerminalClasses: [out] On success, filled in with an array of terminal
//                           class IIDs that are supported by the MSP for this
//                           address.  This value may be NULL, in which case
//                           pdwNumClasses will return the needed buffer size.
//
// Returns:
//     S_OK                   Success.
//     E_POINTER              A pointer argument is invalid.
//     TAPI_E_NOTENOUGHMEMORY The specified buffer is not large enough to
//                                contain all of the available dynamic
//                                terminal classes.


STDMETHODIMP CTerminalManager::GetDynamicTerminalClasses(
        IN     DWORD                dwMediaTypes,
        IN OUT DWORD              * pdwNumClasses,
        OUT    IID                * pTerminalClasses
        )
{ 
    //
    // no shared data = no locking here
    //

    LOG((MSP_TRACE, "CTerminalManager::GetDynamicTerminalClasses - enter"));

    //
    // Check parameters.
    //

    if ( TM_IsBadWritePtr(pdwNumClasses, sizeof(DWORD) ) )
    { 
        LOG((MSP_ERROR, "CTerminalManager::GetDynamicTerminalClasses - "
            "bad NumClasses pointer - returning E_POINTER")); 
        return E_POINTER;
    }

    //
    // Let's find also the temrinals from the registry
    //

    CLSID* pTerminals = NULL;
    DWORD dwTerminals = 0;
    HRESULT hr = E_FAIL;

    hr = CPTUtil::ListTerminalClasses( 
        dwMediaTypes, 
        &pTerminals,
        &dwTerminals
        );

    if( FAILED(hr) )
    {

        LOG((MSP_ERROR, "CTerminalManager::GetDynamicTerminalClasses - "
            "ListTerminalClasses failed - returning 0x%08x", hr)); 
        return hr;
    }

    //
    // If the caller is just asking for the needed buffer size, tell them.
    //

    if (pTerminalClasses == NULL)
    {
        *pdwNumClasses = dwTerminals;
        delete[] pTerminals;

        LOG((MSP_TRACE, "CTerminalManager::GetDynamicTerminalClasses - "
            "provided needed buffer size - "
            "returning S_OK")); 

        return S_OK;
    }

    //
    // Otherwise, the caller is asking for the terminal classes.
    //

    if ( TM_IsBadWritePtr(pTerminalClasses, (*pdwNumClasses) * sizeof(IID) ) )
    { 
        delete[] pTerminals;

        LOG((MSP_ERROR, "CTerminalManager::GetDynamicTerminalClasses - "
            "bad TerminalClasses pointer - returning E_POINTER")); 
        return E_POINTER;
    }

    //
    // See if the caller gave us enough buffer space to return all the terminal
    // classes. If not, tell them so and stop.
    //

    if ( dwTerminals > *pdwNumClasses )
    {
        //
        // Fill in the number of classes that are available.
        //

        *pdwNumClasses = dwTerminals;
        delete[] pTerminals;

        LOG((MSP_ERROR, "CTerminalManager::GetDynamicTerminalClasses - "
            "not enough space for requested info - "
            "returning TAPI_E_NOTENOUGHMEMORY")); 

        return TAPI_E_NOTENOUGHMEMORY;
    }

    //
    // Copy the terminal classes that match this/these media type(s)
    // and direction(s).
    //


    //
    // Copy the terminals from registry
    //

    for( DWORD dwTerminal = 0; dwTerminal < dwTerminals; dwTerminal++)
    {
        pTerminalClasses[dwTerminal] = pTerminals[dwTerminal];
    }

    *pdwNumClasses = dwTerminals;

    delete[] pTerminals;

    LOG((MSP_TRACE, "CTerminalManager::GetDynamicTerminalClasses - exit S_OK"));
    return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// CreateDynamicTerminal
//
// This method is called by the MSP to create a dynamic terminal object.  The
// Terminal Manager verifies that the MediaType and Direction are valid for
// the terminal class being created.  This call AddRef()s the terminal object
// before returning it.
//
// Parameters:
//     iidTerminalClass: [in] IID of the terminal class to be created.
//     dwMediaType:      [in] TAPI media type of the terminal to be created.
//     Direction:        [in] Terminal direction of the terminal to be
//                           created.
//     ppTerminal:       [out] Returned created terminal object
//
// Returns:
//
// S_OK           Success.
// E_POINTER      A pointer argument is invalid.
// E_OUTOFMEMORY  There is not enough memory to create the terminal object.
// E_INVALIDARG   The terminal class is invalid or not supported, or the media
//                    type or direction is invalid for the indicated terminal
//                    class.
//


STDMETHODIMP CTerminalManager::CreateDynamicTerminal(
        IN  IUnknown            * pOuterUnknown,
        IN  IID                   iidTerminalClass,
        IN  DWORD                 dwMediaType,
        IN  TERMINAL_DIRECTION    Direction,
        IN  MSP_HANDLE            htAddress,
        OUT ITTerminal         ** ppTerminal
        )
{
    //
    // no shared data = no locking here
    //

    LOG((MSP_TRACE, "CTerminalManager::CreateDynamicTerminal - enter"));

    //
    // Check parameters.
    // Only one media type can be set.
    //

    if ( (pOuterUnknown != NULL) &&
         IsBadReadPtr(pOuterUnknown, sizeof(IUnknown)) )
    {
        LOG((MSP_ERROR, "CTerminalManager::CreateDynamicTerminal - "
            "outer unknown pointer invalid - returning E_POINTER"));

        return E_POINTER;
    }

    if ( TM_IsBadWritePtr(ppTerminal, sizeof(ITTerminal *)) )
    {
        LOG((MSP_ERROR, "CTerminalManager::CreateDynamicTerminal - "
            "terminal output pointer invalid - returning E_POINTER"));

        return E_POINTER;
    }


    //
    // dwMediaType can be a combination of media types, but it still must be 
    // legal
    //

    if ( !IsValidAggregatedMediaType(dwMediaType) )
    {
        LOG((MSP_ERROR, "CTerminalManager::CreateDynamicTerminal - "
            "ivalid media type [%lx] requested - returning E_INVALIDARG", dwMediaType));

        return E_INVALIDARG;
    }

    //
    // Verify also TD_MULTITRACK_MIXED
    //

    if ( ( Direction != TD_CAPTURE ) && 
         ( Direction != TD_RENDER )  &&
         ( Direction != TD_MULTITRACK_MIXED))
    {
        LOG((MSP_ERROR, "CTerminalManager::CreateDynamicTerminal - "
            "invalid direction requested - returning E_INVALIDARG"));

        return E_INVALIDARG;
    }

    //
    // Declare CLSID for plug-in terminal
    //

    CLSID clsidTerminal = CLSID_NULL;

    //
    // Go to find out the terminal in registry
    //

    HRESULT hr = E_FAIL;
    CPTTerminal Terminal;

    hr = CPTUtil::SearchForTerminal(
        iidTerminalClass,
        dwMediaType,
        Direction,
        &Terminal);

    if( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CTerminalManager::CreateDynamicTerminal - "
            "SearchForTerminal failed - returning 0x%08x", hr));
        return hr;
    }

    //
    // Using the CLSID field in our array entry, CoCreate the dynamic
    // terminal.
    //

    hr = CoCreateInstance(Terminal.m_clsidCOM,
                          pOuterUnknown,
                          CLSCTX_INPROC_SERVER,
                          IID_ITTerminal,
                          (void **) ppTerminal);

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CTerminalManager::CreateDynamicTerminal - "
            "CoCreateInstance of requested terminal failed - "
            "returning 0x%08x", hr));

        return hr;
    }

    //
    // Initialize the dynamic terminal instance with the media type
    // and direction.
    //

    ITPluggableTerminalInitialization * pTerminalInitialization;

    hr = (*ppTerminal)->QueryInterface(IID_ITPluggableTerminalInitialization,
                                       (void **) &pTerminalInitialization);

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CTerminalManager::CreateDynamicTerminal - "
            "QueryInterface for private terminal interface failed - "
            "returning 0x%08x", hr));

        (*ppTerminal)->Release();
        *ppTerminal = NULL;       // make buggy apps more explicitly buggy

        return hr;
    }

    hr = pTerminalInitialization->InitializeDynamic(iidTerminalClass,
                                     dwMediaType,
                                     Direction,
                                     htAddress);

    pTerminalInitialization->Release();

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CTerminalManager::CreateDynamicTerminal - "
            "private Initialize() failed - "
            "returning 0x%08x", hr));

        (*ppTerminal)->Release();
        *ppTerminal = NULL;       // make buggy apps more explicitly buggy

        return hr;
    }

    LOG((MSP_TRACE, "CTerminalManager::CreateDynamicTerminal - "
        "exit S_OK"));
    return S_OK;
}

// ITTerminalManager2

STDMETHODIMP CTerminalManager::GetPluggableSuperclasses(
        IN OUT  DWORD                  * pdwNumSuperclasses,
        OUT     IID                    * pSuperclasses
        )
{
    LOG((MSP_TRACE, "CTerminalManager::GetPluggableSuperclasses - enter"));

    //
    // Check parameters.
    //

    if ( TM_IsBadWritePtr(pdwNumSuperclasses, sizeof(DWORD) ) )
    { 
        LOG((MSP_ERROR, "CTerminalManager::GetPluggableSuperclasses - "
            "bad NumClasses pointer - returning E_POINTER")); 
        return E_POINTER;
    }

    //
    // The SafeArray VAriant for Superclasses
    //

    HRESULT hr = E_FAIL;
    CLSID* pSuperclassesCLSID = NULL;
    DWORD dwSuperclasses = 0;

    hr = CPTUtil::ListTerminalSuperclasses( 
        &pSuperclassesCLSID,
        &dwSuperclasses
        );

    if( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CTerminalManager::GetPluggableSuperclasses - "
            "ListTerminalSuperclasses failed - returning 0x%08x", hr)); 
        return hr;
    }

    //
    // If the caller is just asking for the needed buffer size, tell them.
    //

    if (pSuperclasses == NULL)
    {
        *pdwNumSuperclasses = dwSuperclasses;

        delete[] pSuperclassesCLSID;

        LOG((MSP_TRACE, "CTerminalManager::GetPluggableSuperclasses - "
            "provided needed buffer size - returning S_OK")); 

        return S_OK;
    }

    //
    // Otherwise, the caller is asking for the terminal classes.
    //

    if ( TM_IsBadWritePtr(pSuperclasses, (*pdwNumSuperclasses) * sizeof(IID) ) )
    { 
        delete[] pSuperclassesCLSID;

        LOG((MSP_ERROR, "CTerminalManager::GetPluggableSuperclasses - "
            "bad Superclasses pointer - returning E_POINTER")); 
        return E_POINTER;
    }

    //
    // See if the caller gave us enough buffer space to return all the terminal
    // classes. If not, tell them so and stop.
    //

    if ( dwSuperclasses > *pdwNumSuperclasses )
    {
        //
        // Fill in the number of classes that are available.
        //

        *pdwNumSuperclasses = dwSuperclasses;

        delete[] pSuperclassesCLSID;

        LOG((MSP_ERROR, "CTerminalManager::GetPluggableSuperclasses - "
            "not enough space for requested info - "
            "returning TAPI_E_NOTENOUGHMEMORY")); 

        return TAPI_E_NOTENOUGHMEMORY;
    }

    //
    // Copy the terminal classes that match this/these media type(s)
    // and direction(s).
    //

    for( DWORD dwSuperclass = 0; dwSuperclass < dwSuperclasses; dwSuperclass++)
    {
        pSuperclasses[dwSuperclass] = pSuperclassesCLSID[dwSuperclass];
    }

    *pdwNumSuperclasses = dwSuperclasses;

    // Clean-up
    delete[] pSuperclassesCLSID;
    
    LOG((MSP_TRACE, "CTerminalManager::GetPluggableSuperclasses - exit S_OK"));
    return S_OK;
}

STDMETHODIMP CTerminalManager::GetPluggableTerminalClasses(
        IN      IID                      iidSuperclass,
        IN      DWORD                    dwMediaTypes,
        IN OUT  DWORD                  * pdwNumTerminals,
        OUT     IID                    * pTerminals
        )
{
    LOG((MSP_TRACE, "CTerminalManager::GetPluggableTerminalClasses - enter"));

    //
    // Check parameters.
    //

    if ( TM_IsBadWritePtr(pdwNumTerminals, sizeof(DWORD) ) )
    { 
        LOG((MSP_ERROR, "CTerminalManager::GetPluggableTerminalClasses - "
            "bad NumSuperlasses pointer - returning E_POINTER")); 
        return E_POINTER;
    }

    //
    // Get BSTR for iidSuperclass
    //

    if( dwMediaTypes == 0)
    {
        LOG((MSP_ERROR, "CTerminalManager::GetPluggableTerminalClasses exit -"
            "dwMediaTypes invalid, returns E_INVALIDARG"));

        return E_INVALIDARG;
    }

    if( (dwMediaTypes & (
        ((long)TAPIMEDIATYPE_AUDIO) | 
        ((long)TAPIMEDIATYPE_VIDEO) | 
        ((long)TAPIMEDIATYPE_MULTITRACK))) != dwMediaTypes )
    {
        LOG((MSP_ERROR, "CTerminalManager::GetPluggableTerminalClasses exit -"
            "dwMediaTypes invalid, returns E_INVALIDARG"));

        return E_INVALIDARG;
    }

    //
    // Get the object Superclass
    //

    CPTSuperclass Superclass;
    Superclass.m_clsidSuperclass = iidSuperclass;


    //
    // Get the terminals for this superclass
    //

    CLSID* pTerminalClasses = NULL;
    DWORD dwTerminalClasses = 0;
    HRESULT hr = E_FAIL;
    
    hr = Superclass.ListTerminalClasses( 
        dwMediaTypes, 
        &pTerminalClasses,
        &dwTerminalClasses
        );

    if( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CTerminalManager::GetPluggableTerminalClasses - "
            "ListTerminalClasses failed - returning 0x%08x", hr)); 
        return hr;
    }

    //
    // If the caller is just asking for the needed buffer size, tell them.
    //

    if (pTerminals == NULL)
    {
        *pdwNumTerminals = dwTerminalClasses;

        delete[] pTerminalClasses;

        LOG((MSP_TRACE, "CTerminalManager::GetPluggableTerminalClasses - "
            "provided needed buffer size - "
            "returning S_OK")); 

        return S_OK;
    }

    //
    // Otherwise, the caller is asking for the terminal classes.
    //

    if ( TM_IsBadWritePtr(pTerminals, (*pdwNumTerminals) * sizeof(IID) ) )
    { 
        delete[] pTerminalClasses;

        LOG((MSP_ERROR, "CTerminalManager::GetPluggableTerminalClasses - "
            "bad TerminalClasses pointer - returning E_POINTER")); 
        return E_POINTER;
    }

    //
    // See if the caller gave us enough buffer space to return all the terminal
    // classes. If not, tell them so and stop.
    //

    if ( dwTerminalClasses > *pdwNumTerminals )
    {
        //
        // Fill in the number of classes that are available.
        //

        *pdwNumTerminals = dwTerminalClasses;

        delete[] pTerminalClasses;

        LOG((MSP_ERROR, "CTerminalManager::GetPluggableTerminalClasses - "
            "not enough space for requested info - "
            "returning TAPI_E_NOTENOUGHMEMORY")); 

        return TAPI_E_NOTENOUGHMEMORY;
    }

    //
    // Copy the terminal classes that match this/these media type(s)
    // and direction(s).
    //


    for( DWORD dwTerminal = 0; dwTerminal < dwTerminalClasses; dwTerminal++)
    {
        pTerminals[dwTerminal] = pTerminalClasses[dwTerminal];
    }

    *pdwNumTerminals = dwTerminalClasses;

    // Clean-up
    delete[] pTerminalClasses;
    
    LOG((MSP_TRACE, "CTerminalManager::GetPluggableTerminalClasses - exit S_OK"));
    return S_OK;
}


// eof