//******************************************************************************
//
//  Copyright (c) 1999-2000, Microsoft Corporation, All rights reserved
//
//*****************************************************************************

#include "precomp.h"
#include <stdio.h>
#include "ess.h"
#include "moniprov.h"

CMonitorProvider::CMonitorProvider(CLifeControl* pControl) : TUnkBase(pControl),
    m_pNamespace(NULL), m_pSink(NULL), m_pAssertClass(NULL), m_pRetractClass(NULL), m_pGoingUpClass(NULL), m_pGoingDownClass(NULL), m_pErrorClass( NULL )
{
}

CMonitorProvider::~CMonitorProvider()
{
    Shutdown();
    if(m_pNamespace)
        m_pNamespace->Release();
    if(m_pSink)
        m_pSink->Release();

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

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

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

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

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

HRESULT CMonitorProvider::Shutdown()
{
    CInCritSec ics(&m_cs);

    for(TIterator it = m_mapMonitors.begin(); it != m_mapMonitors.end(); it++)
    {
        delete it->second;
    }
    m_mapMonitors.clear();
    return WBEM_S_NO_ERROR;
}

HRESULT CMonitorProvider::SetNamespace(CEssNamespace* pNamespace)
{
    m_pNamespace = pNamespace;
    m_pNamespace->AddRef();
    
    //
    // Retrieve system classes from the namespace
    //

    if(FAILED(m_pNamespace->GetClass(ASSERT_EVENT_CLASS, &m_pAssertClass)))
		return WBEM_E_CRITICAL_ERROR;
    if(FAILED(m_pNamespace->GetClass(RETRACT_EVENT_CLASS, &m_pRetractClass)))
		return WBEM_E_CRITICAL_ERROR;
    if(FAILED(m_pNamespace->GetClass(GOINGUP_EVENT_CLASS, &m_pGoingUpClass)))
		return WBEM_E_CRITICAL_ERROR;
    if(FAILED(m_pNamespace->GetClass(GOINGDOWN_EVENT_CLASS, &m_pGoingDownClass)))
		return WBEM_E_CRITICAL_ERROR;
    if(FAILED(m_pNamespace->GetClass(MONITORERROR_EVENT_CLASS, &m_pErrorClass)))
		return WBEM_E_CRITICAL_ERROR;

    //
    // Retrieve handle values
    //

    m_pAssertClass->GetPropertyHandleEx(MONITORNAME_EVENT_PROPNAME,
                            0, NULL, &m_lNameHandle);
    m_pAssertClass->GetPropertyHandleEx(MONITOROBJECT_EVENT_PROPNAME,
                            0, NULL, &m_lObjectHandle);
    m_pAssertClass->GetPropertyHandleEx(MONITORCOUNT_EVENT_PROPNAME,
                            0, NULL, &m_lCountHandle);
    m_pAssertClass->GetPropertyHandleEx(MONITORNEW_EVENT_PROPNAME,
                            0, NULL, &m_lNewHandle);

	return S_OK;
}

STDMETHODIMP CMonitorProvider::ProvideEvents(IWbemObjectSink* pSink, 
                                                long lFlags)
{
    return pSink->QueryInterface(IID_IWbemEventSink, (void**)&m_pSink);
}

HRESULT CMonitorProvider::AddMonitor(LPCWSTR wszName, LPCWSTR wszQuery, 
                            long lFlags, IWbemContext* pContext)
{
    HRESULT hres;

    CMonitor* pMonitor = new CMonitor;
    if(pMonitor == NULL)
        return WBEM_E_OUT_OF_MEMORY;

    CFiringMonitorCallback* pCallback = 
        new CFiringMonitorCallback(this, wszName);
    if(pCallback == NULL || pCallback->GetName() == NULL)
        return WBEM_E_OUT_OF_MEMORY;
    pCallback->AddRef();
	CTemplateReleaseMe<CFiringMonitorCallback> rm1(pCallback);

	hres = pCallback->Initialize();
	if(FAILED(hres))
	{
		delete pMonitor;
		return hres;
	}
    
    hres = pMonitor->Construct(m_pNamespace, pCallback, wszQuery);
    if(FAILED(hres))
    {
        delete pMonitor;
        return hres;
    }

    //
    // Attempt to start the monitor. TBD: consider monitor's guard
    //

    hres = pMonitor->Start(pContext);
    if(FAILED(hres))
    {
        //
        // Could not start monitor --- depending on whether strong validation
        // is required, remove the monitor or keep it inactive
        //
        
        if(lFlags & WBEM_FLAG_STRONG_VALIDATION)
        {
            delete pMonitor;
            return hres;
        }
    }

    //
    // Add the monitor to the list, active or not
    //

    {
        CInCritSec ics(&m_cs);
        m_mapMonitors[wszName] = pMonitor;
    }

    return hres;
}

HRESULT CMonitorProvider::RemoveMonitor(LPCWSTR wszName, IWbemContext* pContext)
{
    CInCritSec ics(&m_cs);
    
    TIterator it = m_mapMonitors.find(wszName);
    if(it == m_mapMonitors.end())
        return WBEM_S_FALSE;

    it->second->Stop(pContext);
    delete it->second;
    m_mapMonitors.erase(it);

    return WBEM_S_NO_ERROR;
}

// static
HRESULT CMonitorProvider::GetMonitorInfo(IWbemClassObject* pMonitorObj,
                                BSTR* pstrKey, BSTR* pstrQuery, long* plFlags)
{
    HRESULT hres;

    if(pstrKey)
    {
        // 
        // Extract the relpath to use as the key
        //
        
        VARIANT v;
        hres = pMonitorObj->Get(MONITOR_NAME_PROPNAME, 0, &v, NULL, NULL);
        if(FAILED(hres))
        {
            ERRORTRACE((LOG_ESS, "Monitor without a path? Not valid\n"));
            return hres;
        }

        if(V_VT(&v) != VT_BSTR)
            return WBEM_E_CRITICAL_ERROR;

        *pstrKey = V_BSTR(&v);
    }

    if(pstrQuery)
    {
        //
        // Check query type
        //

        VARIANT vType;
        VariantInit(&vType);
        CClearMe cm1(&vType);

        hres = pMonitorObj->Get(MONITOR_QUERYLANG_PROPNAME, 0, &vType, NULL, 
                                    NULL);
        if(FAILED(hres))
        {
            ERRORTRACE((LOG_ESS, "Monitor without a query type? Not valid\n"));
            return hres;
        }

        if(V_VT(&vType) != VT_BSTR)
        {
            ERRORTRACE((LOG_ESS, "Monitor without a query type? Not valid\n"));
            return WBEM_E_INVALID_OBJECT;
        }

        if(wbem_wcsicmp(V_BSTR(&vType), L"WQL"))
        {
            ERRORTRACE((LOG_ESS, "Monitor with invalid query type %S is "
                        "rejected\n", V_BSTR(&vType)));
            return hres;
        }

        // 
        // Extract the query 
        //
        
        VARIANT v;
        hres = pMonitorObj->Get(MONITOR_QUERY_PROPNAME, 0, &v, NULL, NULL);
        if(FAILED(hres))
        {
            ERRORTRACE((LOG_ESS, "Monitor without a name? Not valid\n"));
            return hres;
        }

        if(V_VT(&v) != VT_BSTR)
            return hres;

        *pstrQuery = V_BSTR(&v);
    }

    if(plFlags)
    {
        // 
        // TBD: no flags for now
        //
        
        *plFlags = 0;
    }

    return WBEM_S_NO_ERROR;
}


HRESULT CMonitorProvider::ConstructAssert(LPCWSTR wszName, _IWmiObject* pObj, 
                                bool bEvent, 
                                DWORD dwTotalCount, _IWmiObject** ppEvent)
{
    HRESULT hres;
    hres = GetInstance(m_pAssertClass, wszName, dwTotalCount, ppEvent);
    if(FAILED(hres))
        return hres;
    hres = SetObject(*ppEvent, pObj, bEvent);
    if(FAILED(hres))
        (*ppEvent)->Release();
    return hres;
}
    
HRESULT CMonitorProvider::ConstructRetract(LPCWSTR wszName, _IWmiObject* pObj, bool bEvent, 
                                DWORD dwTotalCount, _IWmiObject** ppEvent)
{
    HRESULT hres;
    hres = GetInstance(m_pRetractClass, wszName, dwTotalCount, ppEvent);
    if(FAILED(hres))
        return hres;
    hres = SetObject(*ppEvent, pObj, bEvent);
    if(FAILED(hres))
        (*ppEvent)->Release();
    return hres;
}

HRESULT CMonitorProvider::ConstructGoingUp(LPCWSTR wszName, DWORD dwNumMatching, 
                                _IWmiObject** ppEvent)
{
    return GetInstance(m_pGoingUpClass, wszName, dwNumMatching, ppEvent);
}

HRESULT CMonitorProvider::ConstructGoingDown(LPCWSTR wszName, DWORD dwNumMatching, 
                                _IWmiObject** ppEvent)
{
    return GetInstance(m_pGoingDownClass, wszName, dwNumMatching, ppEvent);
}

HRESULT CMonitorProvider::ConstructError(LPCWSTR wszName, HRESULT hresError, 
                                IWbemClassObject* pErrorObj, 
                                _IWmiObject** ppEvent)
{
    return GetInstance(m_pErrorClass, wszName, 0, ppEvent);
}

HRESULT CMonitorProvider::GetInstance(_IWmiObject* pClass, LPCWSTR wszName, DWORD dwCount,
                                        _IWmiObject** ppEvent)
{
    if(pClass == NULL)
        return WBEM_E_CRITICAL_ERROR;

    HRESULT hres;
    IWbemClassObject* pEvent = NULL;
    hres = pClass->SpawnInstance(0, &pEvent);
    if(FAILED(hres))
        return hres;
    pEvent->QueryInterface(IID__IWmiObject, (void**)ppEvent);
    pEvent->Release();

    hres = (*ppEvent)->SetPropByHandle(m_lCountHandle, 0, sizeof(DWORD), 
                                        &dwCount);
    if(FAILED(hres))
    {
        (*ppEvent)->Release();
        return hres;
    }

    hres = (*ppEvent)->SetPropByHandle(m_lNameHandle, 0, 
                                        wcslen(wszName)*2+2, (LPVOID)wszName);
    if(FAILED(hres))
    {
        (*ppEvent)->Release();
        return hres;
    }

    return WBEM_S_NO_ERROR;
}
    
HRESULT CMonitorProvider::SetObject(_IWmiObject* pEvent, _IWmiObject* pObj,
                                        bool bFromEvent)
{
    HRESULT hres;

    hres = pEvent->SetPropByHandle(m_lObjectHandle, 0, sizeof(_IWmiObject*), 
                                        &pObj);
    if(FAILED(hres))
        return hres;

    short bNew = (bFromEvent?-1:0);
    hres = pEvent->SetPropByHandle(m_lNewHandle, 0, sizeof(short), &bNew);
    if(FAILED(hres))
        return hres;

    return WBEM_S_NO_ERROR;
}
    
    


CFiringMonitorCallback::CFiringMonitorCallback(CMonitorProvider* pProvider,
                                                LPCWSTR wszName)
    : m_pProvider(pProvider), m_wsName(wszName), m_pSink(NULL), m_lRef(0)
{
    if(m_pProvider)
        m_pProvider->AddRef();
}

CFiringMonitorCallback::~CFiringMonitorCallback()
{
    if(m_pProvider)
        m_pProvider->Release();
    if(m_pSink)
        m_pSink->Release();
}

ULONG STDMETHODCALLTYPE CFiringMonitorCallback::AddRef()
{
    return InterlockedIncrement(&m_lRef);
}

ULONG STDMETHODCALLTYPE CFiringMonitorCallback::Release()
{
    long lRef = InterlockedIncrement(&m_lRef);
    if(lRef == 0)
        delete this;
    return lRef;
}

HRESULT CFiringMonitorCallback::Initialize()
{
    //
    // Get us a restricted sink for our key.  TBD
    //

    m_pSink = m_pProvider->GetSink();
    m_pSink->AddRef();
    return S_OK;
}

bool CFiringMonitorCallback::CheckSink()
{
    return (m_pSink && (m_pSink->IsActive() == WBEM_S_NO_ERROR));
}

HRESULT CFiringMonitorCallback::FireEvent(_IWmiObject* pEvent)
{
    return m_pSink->Indicate(1, (IWbemClassObject**)&pEvent);
}

HRESULT CFiringMonitorCallback::Assert(_IWmiObject* pObj, LPCWSTR wszPath,
                        bool bEvent, DWORD dwTotalCount)
{
    if(!CheckSink())
        return WBEM_S_FALSE;

    HRESULT hres;
    _IWmiObject* pEvent = NULL;
    hres = m_pProvider->ConstructAssert(m_wsName, pObj, bEvent, dwTotalCount, &pEvent);
    if(FAILED(hres))
        return hres;
    CReleaseMe rm(pEvent);
    
    return FireEvent(pEvent);
}
    
HRESULT CFiringMonitorCallback::Retract(_IWmiObject* pObj, LPCWSTR wszPath,
                            bool bEvent, DWORD dwTotalCount)
{
    if(!CheckSink())
        return WBEM_S_FALSE;

    HRESULT hres;
    _IWmiObject* pEvent = NULL;
    hres = m_pProvider->ConstructRetract(m_wsName, pObj, bEvent, dwTotalCount, &pEvent);
    if(FAILED(hres))
        return hres;
    CReleaseMe rm(pEvent);
    
    return FireEvent(pEvent);
}

HRESULT CFiringMonitorCallback::GoingUp(DWORD dwNumMatching)
{
    if(!CheckSink())
        return WBEM_S_FALSE;

    HRESULT hres;
    _IWmiObject* pEvent = NULL;
    hres = m_pProvider->ConstructGoingUp(m_wsName, dwNumMatching, &pEvent);
    if(FAILED(hres))
        return hres;
    CReleaseMe rm(pEvent);
    
    return FireEvent(pEvent);
}

HRESULT CFiringMonitorCallback::GoingDown(DWORD dwNumMatching)
{
    if(!CheckSink())
        return WBEM_S_FALSE;

    HRESULT hres;
    _IWmiObject* pEvent = NULL;
    hres = m_pProvider->ConstructGoingDown(m_wsName, dwNumMatching, &pEvent);
    if(FAILED(hres))
        return hres;
    CReleaseMe rm(pEvent);
    
    return FireEvent(pEvent);
}

HRESULT CFiringMonitorCallback::Error(HRESULT hresError, 
                                        IWbemClassObject* pErrorObj)
{
    if(!CheckSink())
        return WBEM_S_FALSE;

    HRESULT hres;
    _IWmiObject* pEvent = NULL;
    hres = m_pProvider->ConstructError(m_wsName, hresError, pErrorObj, &pEvent);
    if(FAILED(hres))
        return hres;
    CReleaseMe rm(pEvent);
    
    return FireEvent(pEvent);
}