/*++

Copyright (C) Microsoft Corporation, 1997 - 1999

Module Name:

    cpubfilt.cxx

Abstract:

    This file contains the implementation of the PublisherFilter
    for the ISensNetwork interface exposed by SENS.

Author:

    Gopal Parupudi    <GopalP>

[Notes:]

    optional-notes

Revision History:

    GopalP          1/26/1998         Start.

--*/



#include <precomp.hxx>


//
// Globals
//
LONG g_cFilterObj;      // Count of active components
LONG g_cFilterLock;     // Count of Server locks





//
// Constructors and Destructors
//
CImpISensNetworkFilter::CImpISensNetworkFilter(
    void
    ) : m_cRef(1L), // Add a reference.
        m_pConnectionMade_Enum(NULL),
        m_pConnectionMadeNoQOC_Enum(NULL),
        m_pConnectionLost_Enum(NULL),
        m_pDestinationReachable_Enum(NULL),
        m_pDestinationReachableNoQOC_Enum(NULL),
        m_pConnectionMade_FiringControl(NULL),
        m_pConnectionMadeNoQOC_FiringControl(NULL),
        m_pConnectionLost_FiringControl(NULL),
        m_pDestinationReachable_FiringControl(NULL),
        m_pDestinationReachableNoQOC_FiringControl(NULL)
{
    InterlockedIncrement(&g_cFilterObj);
}

CImpISensNetworkFilter::~CImpISensNetworkFilter(
    void
    )
{
    InterlockedDecrement(&g_cFilterObj);

    //
    // Release all references that we added to IEnumEventObject pointers,
    // if any.
    //
    if (m_pConnectionMade_Enum)
        {
        m_pConnectionMade_Enum->Release();
        }

    if (m_pConnectionMadeNoQOC_Enum)
        {
        m_pConnectionMadeNoQOC_Enum->Release();
        }

    if (m_pConnectionLost_Enum)
        {
        m_pConnectionLost_Enum->Release();
        }

    if (m_pDestinationReachable_Enum)
        {
        m_pDestinationReachable_Enum->Release();
        }

    if (m_pDestinationReachableNoQOC_Enum)
        {
        m_pDestinationReachableNoQOC_Enum->Release();
        }

    //
    // Release all references that we added to IFiringControl pointers,
    // if any (and there shouldn't be any).
    //
    if (m_pConnectionMade_FiringControl)
        {
        m_pConnectionMade_FiringControl->Release();
        }

    if (m_pConnectionMadeNoQOC_FiringControl)
        {
        m_pConnectionMadeNoQOC_FiringControl->Release();
        }

    if (m_pConnectionLost_FiringControl)
        {
        m_pConnectionLost_FiringControl->Release();
        }

    if (m_pDestinationReachable_FiringControl)
        {
        m_pDestinationReachable_FiringControl->Release();
        }

    if (m_pDestinationReachableNoQOC_FiringControl)
        {
        m_pDestinationReachableNoQOC_FiringControl->Release();
        }
}




//
// Standard QueryInterface
//
STDMETHODIMP
CImpISensNetworkFilter::QueryInterface(
    REFIID riid,
    LPVOID *ppv
    )
{
    HRESULT hr;

    DebugTraceGuid("CImpISensNetworkFilter:QueryInterface()", riid);

    hr = S_OK;
    *ppv = NULL;

    // IUnknown
    if (IsEqualIID(riid, IID_IUnknown))
        {
        *ppv = (ISensNetwork *) this;
        }
    else
    // ISensNetwork
    if (IsEqualIID(riid, IID_ISensNetwork))
        {
        *ppv = (ISensNetwork *) this;
        }
    else
    // IPublisherFilter
    if (IsEqualIID(riid, IID_IPublisherFilter))
        {
        *ppv = (IPublisherFilter *) this;
        }
    else
        {
        hr = E_NOINTERFACE;
        }

    if (NULL != *ppv)
        {
        ((LPUNKNOWN)*ppv)->AddRef();
        }

    return hr;
}




//
// IDispatch member function implementations. These are dummy implementations
// as EventSystem never calls them.
//

STDMETHODIMP
CImpISensNetworkFilter::GetTypeInfoCount(
    UINT *pCountITypeInfo
    )
{
    return E_NOTIMPL;

}

STDMETHODIMP
CImpISensNetworkFilter::GetTypeInfo(
    UINT iTypeInfo,
    LCID lcid,
    ITypeInfo **ppITypeInfo
    )
{
    return E_NOTIMPL;
}

STDMETHODIMP
CImpISensNetworkFilter::GetIDsOfNames(
    REFIID riid,
    LPOLESTR *arrNames,
    UINT cNames,
    LCID lcid,
    DISPID *arrDispIDs)
{
    return E_NOTIMPL;
}

STDMETHODIMP
CImpISensNetworkFilter::Invoke(
    DISPID dispID,
    REFIID riid,
    LCID lcid,
    WORD wFlags,
    DISPPARAMS *pDispParams,
    VARIANT *pvarResult,
    EXCEPINFO *pExecpInfo,
    UINT *puArgErr
    )
{
    return E_NOTIMPL;
}




//
// Standard AddRef and Release
//
STDMETHODIMP_(ULONG)
CImpISensNetworkFilter::AddRef(
    void
    )
{
    return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG)
CImpISensNetworkFilter::Release(
    void
    )
{
    LONG cRefT;

    SensPrint(SENS_INFO, (SENS_STRING("\t| CImpISensNetworkFilter::Release(m_cRef = %d) called.\n"), m_cRef));

    cRefT = InterlockedDecrement((PLONG) &m_cRef);

    if (0 == m_cRef)
        {
        delete this;
        }

    return cRefT;
}



//
// ISensNetwork Implementation.
//

STDMETHODIMP
CImpISensNetworkFilter::ConnectionMade(
    BSTR bstrConnection,
    ULONG ulType,
    LPSENS_QOCINFO lpQOCInfo
    )
{
    SensPrint(SENS_INFO, (SENS_STRING("---------------------------------------------------------\n")));
    SensPrint(SENS_INFO, (SENS_STRING("CImpISensNetworkFilter::ConnectionMade() called\n\n")));
    SensPrintW(SENS_INFO, (SENS_BSTR("    bstrConnection - %s\n"), bstrConnection));
    SensPrint(SENS_INFO, (SENS_STRING("            ulType - 0x%x\n"), ulType));
    SensPrint(SENS_INFO, (SENS_STRING("---------------------------------------------------------\n")));

    HRESULT hr = S_OK;
    ConnectionFilter filter(PROPERTY_CONNECTION_MADE_TYPE, ulType, hr);
    if (SUCCEEDED(hr))
        {
        hr = FilterAndFire(
                 filter,
                 m_pConnectionMade_Enum,
                 m_pConnectionMade_FiringControl
                 );
        }

    // We're done with this IFiringControl object.
    m_pConnectionMade_FiringControl->Release();
    m_pConnectionMade_FiringControl = NULL;

    return hr;
}


STDMETHODIMP
CImpISensNetworkFilter::ConnectionMadeNoQOCInfo(
    BSTR bstrConnection,
    ULONG ulType
    )
{
    SensPrint(SENS_INFO, (SENS_STRING("---------------------------------------------------------\n")));
    SensPrint(SENS_INFO, (SENS_STRING("CImpISensNetworkFilter::ConnectionMadeNoQOCInfo() called\n\n")));
    SensPrintW(SENS_INFO, (SENS_BSTR("    bstrConnection - %s\n"), bstrConnection));
    SensPrint(SENS_INFO, (SENS_STRING("            ulType - 0x%x\n"), ulType));
    SensPrint(SENS_INFO, (SENS_STRING("---------------------------------------------------------\n")));

    HRESULT hr = S_OK;
    ConnectionFilter filter(PROPERTY_CONNECTION_MADE_NOQOC_TYPE, ulType, hr);
    if (SUCCEEDED(hr))
        {
        hr = FilterAndFire(
                 filter,
                 m_pConnectionMadeNoQOC_Enum,
                 m_pConnectionMadeNoQOC_FiringControl
                 );
        }

    // We're done with this IFiringControl object.
    m_pConnectionMadeNoQOC_FiringControl->Release();
    m_pConnectionMadeNoQOC_FiringControl = NULL;

    return hr;
}


STDMETHODIMP
CImpISensNetworkFilter::ConnectionLost(
    BSTR bstrConnection,
    ULONG ulType
    )
{
    SensPrint(SENS_INFO, (SENS_STRING("---------------------------------------------------------\n")));
    SensPrint(SENS_INFO, (SENS_STRING("CImpISensNetworkFilter::ConnectionLost() called\n\n")));
    SensPrintW(SENS_INFO, (SENS_BSTR("    bstrConnection - %s\n"), bstrConnection));
    SensPrint(SENS_INFO, (SENS_STRING("            ulType - 0x%x\n"), ulType));
    SensPrint(SENS_INFO, (SENS_STRING("---------------------------------------------------------\n")));

    HRESULT hr = S_OK;
    ConnectionFilter filter(PROPERTY_CONNECTION_LOST_TYPE, ulType, hr);
    if (SUCCEEDED(hr))
        {
        hr = FilterAndFire(
                 filter,
                 m_pConnectionLost_Enum,
                 m_pConnectionLost_FiringControl
                 );
        }

    // We're done with this IFiringControl object.
    m_pConnectionLost_FiringControl->Release();
    m_pConnectionLost_FiringControl = NULL;

    return hr;
}


STDMETHODIMP
CImpISensNetworkFilter::DestinationReachable(
    BSTR bstrDestination,
    BSTR bstrConnection,
    ULONG ulType,
    LPSENS_QOCINFO lpQOCInfo
    )
{
    SensPrint(SENS_INFO, (SENS_STRING("---------------------------------------------------------\n")));
    SensPrint(SENS_INFO, (SENS_STRING("CImpISensNetworkFilter::DestinationReachable() called\n\n")));
    SensPrintW(SENS_INFO, (SENS_BSTR("   bstrDestination - %s\n"), bstrDestination));
    SensPrintW(SENS_INFO, (SENS_BSTR("    bstrConnection - %s\n"), bstrConnection));
    SensPrint(SENS_INFO, (SENS_STRING("            ulType - 0x%x\n"), ulType));
    SensPrint(SENS_INFO, (SENS_STRING("---------------------------------------------------------\n")));

    HRESULT hr = S_OK;
    ReachabilityFilter filter(PROPERTY_DESTINATION, PROPERTY_DESTINATION_TYPE, bstrDestination, ulType, hr);
    if (SUCCEEDED(hr))
        {
        hr = FilterAndFire(
                 filter,
                 m_pDestinationReachable_Enum,
                 m_pDestinationReachable_FiringControl
                 );
        }

    // We're done with this IFiringControl object.
    m_pDestinationReachable_FiringControl->Release();
    m_pDestinationReachable_FiringControl = NULL;

    return hr;
}


STDMETHODIMP
CImpISensNetworkFilter::DestinationReachableNoQOCInfo(
    BSTR bstrDestination,
    BSTR bstrConnection,
    ULONG ulType
    )
{
    SensPrint(SENS_INFO, (SENS_STRING("---------------------------------------------------------\n")));
    SensPrint(SENS_INFO, (SENS_STRING("CImpISensNetworkFilter::DestinationReachableNoQOCInfo() called\n\n")));
    SensPrintW(SENS_INFO, (SENS_BSTR("   bstrDestination - %s\n"), bstrDestination));
    SensPrintW(SENS_INFO, (SENS_BSTR("    bstrConnection - %s\n"), bstrConnection));
    SensPrint(SENS_INFO, (SENS_STRING("            ulType - 0x%x\n"), ulType));
    SensPrint(SENS_INFO, (SENS_STRING("---------------------------------------------------------\n")));


    HRESULT hr = S_OK;
    ReachabilityFilter filter(PROPERTY_DESTINATION_NOQOC, PROPERTY_DESTINATION_NOQOC_TYPE, bstrDestination, ulType, hr);
    if (SUCCEEDED(hr))
        {
        hr = FilterAndFire(
                 filter,
                 m_pDestinationReachableNoQOC_Enum,
                 m_pDestinationReachableNoQOC_FiringControl
                 );
        }

    // We're done with this IFiringControl object.
    m_pDestinationReachableNoQOC_FiringControl->Release();
    m_pDestinationReachableNoQOC_FiringControl = NULL;

    return hr;
}




//
// IPublisherFilter Implementation.
//
STDMETHODIMP
CImpISensNetworkFilter::Initialize(
    BSTR bstrMethodName,
    IDispatch* dispUserDefined
    )
{
    HRESULT hr = E_INVALIDARG;

    IEnumEventObject** ppEnum = NULL;
    if (wcscmp(bstrMethodName, CONNECTION_MADE_METHOD) == 0)
        {
        ppEnum = &m_pConnectionMade_Enum;
        }
    else
    if (wcscmp(bstrMethodName, CONNECTION_MADE_NOQOC_METHOD) == 0)
        {
        ppEnum = &m_pConnectionMadeNoQOC_Enum;
        }
    else
    if (wcscmp(bstrMethodName, CONNECTION_LOST_METHOD) == 0)
        {
        ppEnum = &m_pConnectionLost_Enum;
        }
    else
    if (wcscmp(bstrMethodName, DESTINATION_REACHABLE_METHOD) == 0)
        {
        ppEnum = &m_pDestinationReachable_Enum;
        }
    else
    if (wcscmp(bstrMethodName, DESTINATION_REACHABLE_NOQOC_METHOD) == 0)
        {
        ppEnum = &m_pDestinationReachableNoQOC_Enum;
        }

    if (ppEnum != NULL)
        {
        IEventControl* control = NULL;
        hr = dispUserDefined->QueryInterface(IID_IEventControl, (void**)&control);
        if (FAILED(hr))
            {
            return hr;
            }

        IEventObjectCollection* collection = NULL;
        hr = control->GetSubscriptions(bstrMethodName, NULL, NULL, &collection);
        if (SUCCEEDED(hr))
            {
            hr = collection->get_NewEnum(ppEnum);

            // Don't need the collection any more... just keep the enum
            collection->Release();
            }

        control->Release();
        }

        return hr;
}


STDMETHODIMP
CImpISensNetworkFilter::PrepareToFire(
    BSTR bstrMethodName,
    IFiringControl* pIFiringControl
    )
{
    HRESULT hr = E_INVALIDARG;

    IFiringControl** ppFiringControl = NULL;
    if (wcscmp(bstrMethodName, CONNECTION_MADE_METHOD) == 0)
        {
        ppFiringControl = &m_pConnectionMade_FiringControl;
        }
    else
    if (wcscmp(bstrMethodName, CONNECTION_MADE_NOQOC_METHOD) == 0)
        {
        ppFiringControl = &m_pConnectionMadeNoQOC_FiringControl;
        }
    else
    if (wcscmp(bstrMethodName, CONNECTION_LOST_METHOD) == 0)
        {
        ppFiringControl = &m_pConnectionLost_FiringControl;
        }
    else
    if (wcscmp(bstrMethodName, DESTINATION_REACHABLE_METHOD) == 0)
        {
        ppFiringControl = &m_pDestinationReachable_FiringControl;
        }
    else
    if (wcscmp(bstrMethodName, DESTINATION_REACHABLE_NOQOC_METHOD) == 0)
        {
        ppFiringControl = &m_pDestinationReachableNoQOC_FiringControl;
        }

    if (ppFiringControl != NULL)
        {
        *ppFiringControl = pIFiringControl;
        pIFiringControl->AddRef();
        hr = S_OK;
        }

    return hr;
}




//
// Filter helper implementations.
//


//
// ConnectionFilter implementation.
//

ConnectionFilter::ConnectionFilter(
    const wchar_t* connectionTypeProperty,
    ULONG connectionType,
    HRESULT& hr
    ) : m_connectionTypeProperty(SysAllocString(connectionTypeProperty)),
        m_connectionType(connectionType)
{
    if (m_connectionTypeProperty == NULL)
        {
        hr = E_OUTOFMEMORY;
        }
    else
        {
        hr = S_OK;
        }
}


ConnectionFilter::~ConnectionFilter(
    void
    )
{
    SysFreeString(m_connectionTypeProperty);
}


HRESULT
ConnectionFilter::CheckMatch(
    IEventSubscription* pSubscription
    ) const
{
    VARIANT value;
    VariantInit(&value);
    HRESULT hr = pSubscription->GetPublisherProperty(m_connectionTypeProperty, &value);

    if (hr == S_FALSE)
        {
        // If the property isn't present, consider it a successful match.
        hr = S_OK;
        }
    else
    if (hr == S_OK)
        {
        // If the property is there, it must match the incoming parameter value.
        ASSERT(value.vt == VT_UI4);
        SensPrintW(SENS_INFO, (SENS_BSTR("\t| Property %s has value 0x%x\n"), m_connectionTypeProperty, value.ulVal));
        hr = (m_connectionType == value.ulVal) ? S_OK : S_FALSE;
        }

    VariantClear(&value);

    return hr;
}



//
// ReachabilityFilter implementation
//

ReachabilityFilter::ReachabilityFilter(
    const wchar_t* destinationProperty,
    const wchar_t* destinationTypeProperty,
    BSTR destination,
    ULONG destinationType,
    HRESULT& hr
    ) : m_destinationProperty(SysAllocString(destinationProperty)),
        m_destinationTypeProperty(SysAllocString(destinationTypeProperty)),
        m_destination(destination),
        m_destinationType(destinationType)
{
    if (m_destinationProperty == NULL || m_destinationTypeProperty == NULL)
        {
        hr = E_OUTOFMEMORY;
        }
    else
        {
        hr = S_OK;
        }
}


ReachabilityFilter::~ReachabilityFilter(
    void
    )
{
    SysFreeString(m_destinationProperty);
    SysFreeString(m_destinationTypeProperty);
}


HRESULT
ReachabilityFilter::CheckMatch(
    IEventSubscription* pSubscription
    ) const
{
    HRESULT hr;

    // Check the destination property
    VARIANT value;
    VariantInit(&value);
    hr = pSubscription->GetPublisherProperty(m_destinationProperty, &value);

    if (hr == S_FALSE)
        {
        // If the property isn't present, consider it a successful match.
        hr = S_OK;
        SensPrintW(SENS_INFO, (SENS_BSTR("\t\t\t| Subscription (0x%x) has no %s Dest Property\n"), pSubscription, m_destinationProperty));
        }
    else
    if (hr == S_OK)
        {
        // If the property is there, it must match the incoming parameter value.
        ASSERT(value.vt == VT_BSTR);
        SensPrintW(SENS_INFO, (SENS_BSTR("\t\t\t| Property %s has value %s\n"), m_destinationProperty, value.bstrVal));
        hr = (wcscmp(m_destination, value.bstrVal) == 0) ? S_OK : S_FALSE;
        }

    VariantClear(&value);

    if (hr == S_OK)
        {
        // If we have a match so far, check the destination type property
        VARIANT value;
        VariantInit(&value);
        hr = pSubscription->GetPublisherProperty(m_destinationTypeProperty, &value);

        if (hr == S_FALSE)
            {
            // If the property isn't present, consider it a successful match.
            hr = S_OK;
            SensPrintW(SENS_INFO, (SENS_BSTR("\t\t\t| Subscription (0x%x) has no %s Type Property\n"), pSubscription, m_destinationTypeProperty));
            }
        else
        if (hr == S_OK)
            {
            // If the property is there, it must match the incoming parameter value.
            ASSERT(value.vt == VT_UI4);
            SensPrintW(SENS_INFO, (SENS_BSTR("\t\t\t| Property %s has value 0x%x\n"), m_destinationTypeProperty, value.ulVal));
            hr = (m_destinationType == value.ulVal) ? S_OK : S_FALSE;
            }

        VariantClear(&value);
        }

    return hr;
}




//
// Generic filter and fire method.
//
HRESULT
FilterAndFire(
    const Filter& filter,
    IEnumEventObject* enumerator,
    IFiringControl* firingControl
    )
{
    HRESULT hr;
    IEventSubscription* pSubscription = NULL;
    int i = 0;

    //
    // Reset the enum back to the start
    //
    hr = enumerator->Reset();

    //
    // Loop through all the candidate subscriptions and fire the ones
    // that match the filter criteria.
    //
    for (;;)
        {
        ULONG cCount = 1;

        hr = enumerator->Next(cCount, (IUnknown**)&pSubscription, &cCount);
        if (hr != S_OK || cCount != 1)
            {
            break;
            }

        SensPrintA(SENS_INFO, ("\t\t\t| ****** Count for 0x%x is %d\n", enumerator, ++i));

        hr = filter.CheckMatch(pSubscription);
        if (hr == S_OK)
            {
            SensPrintA(SENS_INFO, ("\t\t\t| FilterAndFire(0x%x): CheckMatch() succeeded\n", enumerator));
            firingControl->FireSubscription(pSubscription);
            }

        pSubscription->Release();
        pSubscription = NULL;
        }

    //
    // Cleanup, including any left-over stuff in case of premature exit from the loop.
    //
    if (pSubscription != NULL)
        {
        pSubscription->Release();
        }

    return S_OK;
}