/*++

Copyright (c) 2000 Microsoft Corporation

Module Name:

    qcstream.cpp

Abstract:

    Implementation of CStreamQualityControlRelay

    Data stored in this class can be, and better, kept in stream object itself,
    because other members in stream need to be accessed to either set or get
    properties related to stream quality control. Most of these access methods
    are specific to each particular kind of stream class.

    This class is used as a data store.

Author:

    Qianbo Huai (qhuai) 03/10/2000

--*/

#include "stdafx.h"

/*//////////////////////////////////////////////////////////////////////////////
////*/
CStreamQualityControlRelay::CStreamQualityControlRelay ()
    :m_pIInnerCallQC (NULL)

    ,m_PrefFlagBitrate (TAPIControl_Flags_Auto)
    ,m_lPrefMaxBitrate (QCDEFAULT_QUALITY_UNSET)
    ,m_lAdjMaxBitrate (QCDEFAULT_QUALITY_UNSET)

    ,m_PrefFlagFrameInterval (TAPIControl_Flags_Auto)
    ,m_lPrefMinFrameInterval (QCDEFAULT_QUALITY_UNSET)
    ,m_lAdjMinFrameInterval (QCDEFAULT_QUALITY_UNSET)

    ,m_fQOSAllowedToSend (TRUE)
    ,m_dwState (NULL)
{
}

/*//////////////////////////////////////////////////////////////////////////////
Description:

    destructor. deregister relay
////*/
CStreamQualityControlRelay::~CStreamQualityControlRelay ()
{
    ENTER_FUNCTION ("CStreamQualityControlRelay::~CStreamQualityControlRelay");

    if (m_pIInnerCallQC)
    {
        LOG ((MSP_ERROR, "!!! %s destructed before unnlink. call keeps stream qc"));

        // access to m_pIInnerCallQC is locked in this method
        UnlinkInnerCallQC (NULL);
    }
}

/*//////////////////////////////////////////////////////////////////////////////
Description:
    store call controller
////*/
HRESULT
CStreamQualityControlRelay::LinkInnerCallQC (
    IN IInnerCallQualityControl *pIInnerCallQC
    )
{
    ENTER_FUNCTION ("CStreamQualityControlRelay::LinkInnerCallQC");

    // check pointer
    if (IsBadReadPtr (pIInnerCallQC, sizeof (IInnerCallQualityControl)))
    {
        LOG ((MSP_ERROR, "%s got bad read pointer", __fxName));
        return E_POINTER;
    }

    // check if call controller already set
    if (NULL != m_pIInnerCallQC)
    {
        LOG ((MSP_WARN, "%s already set call controller", __fxName));
        return E_UNEXPECTED;
    }

    m_pIInnerCallQC = pIInnerCallQC;
    m_pIInnerCallQC->InnerCallAddRef ();

    return S_OK;
}

/*//////////////////////////////////////////////////////////////////////////////
////*/
HRESULT
CStreamQualityControlRelay::UnlinkInnerCallQC (
    IN  IInnerStreamQualityControl *pIInnerStreamQC
    )
{
    ENTER_FUNCTION ("CStreamQualityControlRelay::UnlinkInnerCallQC");

    if (!m_pIInnerCallQC)
    {
        LOG ((MSP_WARN, "%s tried unlink while inner call qc is null", __fxName));
        return S_OK;
    }

    if (NULL != pIInnerStreamQC)
    {
        HRESULT hr;

        // release is initiated by stream, need to remove the link on call
        if (FAILED (hr = m_pIInnerCallQC->DeRegisterInnerStreamQC (pIInnerStreamQC)))
            LOG ((MSP_ERROR, "%s failed to deregister from call qc, %x", __fxName, hr));
    }

    m_pIInnerCallQC->InnerCallRelease ();
    m_pIInnerCallQC = NULL;

    return S_OK;
}

/*//////////////////////////////////////////////////////////////////////////////
////*/
HRESULT
CStreamQualityControlRelay::Get(
    IN  InnerStreamQualityProperty property,
    OUT LONG *plValue,
    OUT TAPIControlFlags *plFlags
    )
{
    ENTER_FUNCTION ("CStreamQualityControlRelay::Get");

    HRESULT hr;

    hr = S_OK;

    switch (property)
    {
    case InnerStreamQuality_PrefMaxBitrate:
        *plValue = m_lPrefMaxBitrate;
        *plFlags = m_PrefFlagBitrate;
        break;

    case InnerStreamQuality_AdjMaxBitrate:
        *plValue = m_lAdjMaxBitrate;
        *plFlags = m_PrefFlagBitrate;
        break;

    case InnerStreamQuality_PrefMinFrameInterval:
        *plValue = m_lPrefMinFrameInterval;
        *plFlags = m_PrefFlagFrameInterval;
        break;

    case InnerStreamQuality_AdjMinFrameInterval:
        *plValue = m_lAdjMinFrameInterval;
        *plFlags = m_PrefFlagFrameInterval;
        break;

    default:
        hr = E_NOTIMPL;
    }

    return hr;
}

/*//////////////////////////////////////////////////////////////////////////////
////*/
HRESULT
CStreamQualityControlRelay::Set(
    IN  InnerStreamQualityProperty property, 
    IN  LONG lValue, 
    IN  TAPIControlFlags lFlags
    )
{
    ENTER_FUNCTION ("CStreamQualityControlRelay::Set");

    HRESULT hr;

    hr = S_OK;

    switch (property)
    {
    case InnerStreamQuality_PrefMaxBitrate:
        if (lValue < QCLIMIT_MIN_BITRATE)
        {
            LOG ((MSP_ERROR, "%s: pref max bitrate %d is too small", __fxName, lValue));
            hr = E_INVALIDARG;
        }
        else
        {
            m_lPrefMaxBitrate = lValue;
            m_PrefFlagBitrate = lFlags;
        }
        break;

    case InnerStreamQuality_AdjMaxBitrate:
        if (lValue < QCLIMIT_MIN_BITRATE)
        {
            LOG ((MSP_ERROR, "%s: adjusted max bitrate %d is too small", __fxName, lValue));
            hr = E_INVALIDARG;
        }
        else
            m_lAdjMaxBitrate = lValue;
        break;

    case InnerStreamQuality_PrefMinFrameInterval:
        if (lValue < QCLIMIT_MIN_FRAME_INTERVAL || lValue > QCLIMIT_MAX_FRAME_INTERVAL)
        {
            LOG ((MSP_ERROR, "%s: pref max frame interval %d is out of range", __fxName, lValue));
            hr = E_INVALIDARG;
        }
        else
        {
            m_lPrefMinFrameInterval = lValue;
            m_PrefFlagFrameInterval = lFlags;
        }
        break;

    case InnerStreamQuality_AdjMinFrameInterval:
        if (lValue < QCLIMIT_MIN_FRAME_INTERVAL || lValue > QCLIMIT_MAX_FRAME_INTERVAL)
        {
            LOG ((MSP_ERROR, "%s: adjusted max frame interval %d is out of range", __fxName, lValue));
            hr = E_INVALIDARG;
        }
        else
            m_lAdjMinFrameInterval = lValue;
        break;

    default:
        hr = E_NOTIMPL;
    }

    return hr;
}