//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1995
//
//  File:       evtsink.cxx
//
//  Contents:   Implementation of the CScriptEventSink class
//
//----------------------------------------------------------------------------

#include "headers.hxx"

CScriptEventSink::CScriptEventSink(CScriptHost *pSH)
{
    _pSH = pSH;
    _ulRefs = 1;

    Assert(_dwSinkCookie == 0);
}

CScriptEventSink::~CScriptEventSink()
{
    Disconnect();
}

//+---------------------------------------------------------------------------
//
//  Member:     CScriptEventSink::Connect, public
//
//  Synopsis:   Connects to event interface on the source object
//
//  Arguments:  [pSource] -- Object to sink events from
//
//  Returns:    HRESULT
//
//              TODO: This method should be more generic and walk through
//              the object's typeinfo looking for the default source
//              interface.
//
//----------------------------------------------------------------------------

HRESULT
CScriptEventSink::Connect(IDispatch *pSource, BSTR bstrProgID)
{
    HRESULT      hr = S_OK;

    IConnectionPointContainer *pCPC;
    IConnectionPoint          *pCP = 0;

    _pDispSource = pSource;

    pSource->AddRef();

    hr = _pDispSource->QueryInterface(IID_IConnectionPointContainer,
                                      (LPVOID*)&pCPC);
    if (!hr)
    {
        if (bstrProgID && SysStringLen(bstrProgID) > 0)
            hr = CLSIDFromProgID(bstrProgID, &_clsidEvents);
        else
            _clsidEvents = DIID_DRemoteMTScriptEvents;

        if (hr == S_OK)
            hr = pCPC->FindConnectionPoint(_clsidEvents, &pCP);

        if (!hr)
        {
            hr = pCP->Advise(this, &_dwSinkCookie);

            ReleaseInterface(pCP);
        }

        ReleaseInterface(pCPC);

#if DBG == 1
        if (hr)
            TraceTag((tagError, "Hookup to event sink returned %x", hr));
#endif
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Member:     CScriptEventSink::Disconnect, public
//
//  Synopsis:   Disconnects from a machine we connected to via Connect().
//
//  Arguments:  (none)
//
//----------------------------------------------------------------------------

void
CScriptEventSink::Disconnect()
{
    HRESULT hr = S_OK;

    if (_dwSinkCookie && _pDispSource)
    {
        IConnectionPointContainer *pCPC;
        IConnectionPoint          *pCP;

        hr = _pDispSource->QueryInterface(IID_IConnectionPointContainer, (LPVOID*)&pCPC);
        if (!hr)
        {
            hr = pCPC->FindConnectionPoint(DIID_DRemoteMTScriptEvents,
                                           &pCP);
            if (!hr)
            {
                pCP->Unadvise(_dwSinkCookie);

                ReleaseInterface(pCP);
            }

            ReleaseInterface(pCPC);

#if DBG == 1
            if (hr)
                TraceTag((tagError, "Unadvise from event sink returned %x", hr));
#endif
        }

        _dwSinkCookie = 0;
    }

    ClearInterface(&_pDispSource);

    _pSH = NULL;
}

// *************************************************************************
//
// CScriptEventSink
//
// Class which implements the event sink for the remote object. We only pay
// attention to Invoke calls.
//
// *************************************************************************

HRESULT
CScriptEventSink::QueryInterface(REFIID iid, void **ppv)
{
    if (iid == IID_IUnknown || 
        iid == IID_IDispatch || 
        iid == _clsidEvents)
    {
        *ppv = (IDispatch *)this;
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    ((IUnknown *)*ppv)->AddRef();
    return S_OK;
}

//---------------------------------------------------------------------------
//
//  Member: CScriptEventSink::GetTypeInfo, IDispatch
//
//---------------------------------------------------------------------------

HRESULT
CScriptEventSink::GetTypeInfo(UINT itinfo, ULONG lcid, ITypeInfo ** pptinfo)
{
    return E_NOTIMPL;
}

//---------------------------------------------------------------------------
//
//  Member: CScriptEventSink::GetTypeInfoCount, IDispatch
//
//---------------------------------------------------------------------------

HRESULT
CScriptEventSink::GetTypeInfoCount(UINT * pctinfo)
{
    *pctinfo = 0;

    return S_OK;
}

//---------------------------------------------------------------------------
//
//  Member: CScriptEventSink::GetIDsOfNames, IDispatch
//
//---------------------------------------------------------------------------

HRESULT
CScriptEventSink::GetIDsOfNames(REFIID riid,
                            LPOLESTR * rgszNames,
                            UINT cNames,
                            LCID lcid,
                            DISPID * rgdispid)
{
    return E_NOTIMPL;
}

//---------------------------------------------------------------------------
//
//  Member: CScriptEventSink::Invoke, IDispatch
//
//---------------------------------------------------------------------------

HRESULT
CScriptEventSink::Invoke(DISPID dispidMember,
                         REFIID riid,
                         LCID lcid,
                         WORD wFlags,
                         DISPPARAMS * pdispparams,
                         VARIANT * pvarResult,
                         EXCEPINFO * pexcepinfo,
                         UINT * puArgErr)
{
    if (_pSH && _pSH->GetSite() && _pSH->GetSite()->_pDispSink)
    {
        DISPPARAMS dp;
        VARIANTARG varg[20];

        // We need to tack on 2 parameters to the call. First is the object
        // that is firing the event. Second is the dispatch ID.
        // We put them on the end of the list, which means they become
        // the first and second parameters to the event.

        if (pdispparams->cArgs > ARRAY_SIZE(varg) - 2)
        {
            AssertSz(FALSE, "NONFATAL: Too many parameters to event (max==18)!");
            return E_FAIL;
        }

        dp.cArgs = pdispparams->cArgs + 2;
        dp.cNamedArgs = pdispparams->cNamedArgs;
        dp.rgdispidNamedArgs = pdispparams->rgdispidNamedArgs;

        memcpy(varg, pdispparams->rgvarg, pdispparams->cArgs * sizeof(VARIANTARG));

        V_VT(&varg[dp.cArgs-1]) = VT_DISPATCH;
        V_DISPATCH(&varg[dp.cArgs-1]) = _pDispSource;

        V_VT(&varg[dp.cArgs-2]) = VT_I4;
        V_I4(&varg[dp.cArgs-2]) = dispidMember;

        dp.rgvarg = varg;

        return _pSH->GetSite()->_pDispSink->Invoke(
                                  DISPID_MTScript_OnEventSourceEvent,
                                  IID_NULL,
                                  lcid,
                                  DISPATCH_METHOD,
                                  &dp,
                                  pvarResult,
                                  pexcepinfo,
                                  puArgErr);
    }

    return S_OK;
}