//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1996 - 1996.
//
//  File:	astgconn.cxx
//
//  Contents:	
//
//  Classes:	
//
//  Functions:	
//
//  History:	03-Apr-96	PhilipLa	Created
//
//----------------------------------------------------------------------------

#include "exphead.cxx"
#pragma hdrstop

#include "astgconn.hxx"
#include <filllkb.hxx>
#include <asyncerr.hxx>


SCODE CAsyncConnection::Init(IConnectionPointContainer *pCPC,
                             CAsyncConnection *pacParent)
{
    SCODE sc = S_OK;
    CConnectionPoint *pcpoint;
    olAssert(_pdacp == NULL);

    if (pacParent)
        _dwAsyncFlags = pacParent->_dwAsyncFlags;
    
    olMem(pcpoint = new CConnectionPoint());
    if ((pacParent) && (_dwAsyncFlags & ASYNC_MODE_COMPATIBILITY))
    {
        pcpoint->SetParent(pacParent->_pdacp);
    }
    else
    {
        pcpoint->SetParent(NULL);
    }

    _pCPC = pCPC;
    _pdacp = pcpoint;
EH_Err:
    return sc;
}


SCODE CAsyncConnection::InitClone(IConnectionPointContainer *pCPC,
                                  CAsyncConnection *pac)
{
    SCODE sc = S_OK;
    CConnectionPoint *pcpoint;
    olAssert(pac != NULL);

    _dwAsyncFlags = pac->_dwAsyncFlags;
    
    olMem(pcpoint = new CConnectionPoint());
    if (_dwAsyncFlags & ASYNC_MODE_COMPATIBILITY)
    {
        IDocfileAsyncConnectionPoint *pdacp;
        if (FAILED(sc = pac->_pdacp->GetParent(&pdacp)))
        {
            delete pcpoint;
            return sc;
        }
        pcpoint->SetParent(pdacp);
    }
    else
    {
        pcpoint->SetParent(NULL);
    }

    _pCPC = pCPC;
    _pdacp = pcpoint;
EH_Err:
    return sc;
}

SCODE CAsyncConnection::InitMarshal(IConnectionPointContainer *pCPC,
                                    DWORD dwAsyncFlags,
                                    IDocfileAsyncConnectionPoint *pdacp)
{
    SCODE sc = S_OK;
    _dwAsyncFlags = dwAsyncFlags;
    
    _pCPC = pCPC;
    _pdacp = pdacp;
    if (_pdacp)
        _pdacp->AddRef();
    return sc;
}


//+---------------------------------------------------------------------------
//
//  Member:	CAsyncConnection::~CAsyncConnection, public
//
//  Synopsis:	Destructor
//
//  Returns:	Appropriate status code
//
//  History:	03-Apr-96	PhilipLa	Created
//
//----------------------------------------------------------------------------

CAsyncConnection::~CAsyncConnection()
{
    olDebugOut((DEB_ITRACE,
                "In  CAsyncConnection::~CAsyncConnection:%p()\n", this));
    //Note:  _pdacp must be released outside of the tree mutex, which
    //  means we need to extract the pointer and release it elsewhere.
#if 0    
    if (_pdacp != NULL)
    {
        _pdacp->Release();
    }
#endif    
    olDebugOut((DEB_ITRACE,
                "Out CAsyncConnection::~CAsyncConnection\n"));
}


//+---------------------------------------------------------------------------
//
//  Member:	CAsyncConnection::QueryInterface, public
//
//  Synopsis:	
//
//  Arguments:	
//
//  Returns:	Appropriate status code
//
//  Modifies:	
//
//  History:	01-Jan-96	SusiA	Created
//
//  Notes:	
//
//----------------------------------------------------------------------------

STDMETHODIMP CAsyncConnection::QueryInterface(REFIID iid, void **ppvObj)
{
    SCODE sc = S_OK;
    olDebugOut((DEB_TRACE,
                  "In  CAsyncConnection::QueryInterface:%p()\n",
                  this));

    *ppvObj = NULL;

    if ((IsEqualIID(iid, IID_IUnknown)) ||
	(IsEqualIID(iid, IID_IConnectionPoint)))
    {
        *ppvObj = (IConnectionPoint *)this;
        CAsyncConnection::AddRef();
    }
    else
    {
        return E_NOINTERFACE;
    }

    olDebugOut((DEB_TRACE, "Out CAsyncConnection::QueryInterface\n"));
    return ResultFromScode(sc);
}



//+---------------------------------------------------------------------------
//
//  Member:	CAsyncConnection::AddRef, public
//
//  Synopsis:	
//
//  Arguments:	
//
//  Returns:	Appropriate status code
//
//  Modifies:	
//
//  History:	29-Dec-95	SusiA	Created
//
//  Notes:	
//
//----------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CAsyncConnection::AddRef(void)
{
    ULONG ulRet;
    olDebugOut((DEB_TRACE,
                  "In  CAsyncConnection::AddRef:%p()\n",
                  this));
    InterlockedIncrement(&_cReferences);
    ulRet = _cReferences;
    
    olDebugOut((DEB_TRACE, "Out CAsyncConnection::AddRef\n"));
    return ulRet;
}


//+---------------------------------------------------------------------------
//
//  Member:	CAsyncConnection::Release, public
//
//  Synopsis:	
//
//  Arguments:	
//
//  Returns:	Appropriate status code
//
//  Modifies:	
//
//  History:	30-Dec-95	SusiA	Created
//
//  Notes:	
//
//----------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CAsyncConnection::Release(void)
{
    LONG lRet;
    olDebugOut((DEB_TRACE,
                  "In  CAsyncConnection::Release:%p()\n",
                  this));

    olAssert(_cReferences > 0);
    lRet = InterlockedDecrement(&_cReferences);

    if (lRet == 0)
    {
        delete this;
    }
    else if (lRet < 0)
    { 
        olAssert((lRet > 0) && "Connection point released too many times.");
        lRet = 0;
    
    }
    olDebugOut((DEB_TRACE, "Out CAsyncConnection::Release\n"));
    return (ULONG)lRet;
}


//+---------------------------------------------------------------------------
//
//  Member:	CAsyncConnection::Notify,  public
//
//  Synopsis:	
//
//  Returns:	Appropriate status code
//
//  History:	14-Jan-96	SusiA	Created
//              27-Feb-96       SusiA   Moved from Async wrappers 
//
//----------------------------------------------------------------------------
SCODE CAsyncConnection::Notify(SCODE scFailure,
                               ILockBytes *pilb,
                               CPerContext *ppc,
                               CSafeSem *pss)
{
    SCODE sc = S_OK;
    BOOL fAccurate = (scFailure == E_PENDING);
    IFillInfo *pfi = ppc->GetFillInfo();
    ULONG ulWaterMark;
    ULONG ulFailurePoint;

    HANDLE hNotifyEvent;

    if (pfi != NULL)
    {
        pfi->GetFailureInfo(&ulWaterMark,
                            &ulFailurePoint);

        pss->Release();
        
        while (((sc = _pdacp->NotifySinks(ulWaterMark,
                                          ulFailurePoint,
                                          fAccurate,
                                          STG_S_MONITORING)) == STG_S_BLOCK) ||
               (sc == STG_S_MONITORING) ||
               // S_OK is a synonym for STG_S_MONITORING
               (sc == S_OK))
        {	
            DWORD dwFlags;

            // wait for an event to signal
            hNotifyEvent = ppc->GetNotificationEvent();
            WaitForSingleObject(hNotifyEvent, INFINITE);

            pfi->GetTerminationStatus(&dwFlags);
            // client terminated call?
            if (dwFlags == TERMINATED_ABNORMAL)
            {
                return STG_E_INCOMPLETE;
            }
            // download is complete
            else if (dwFlags == TERMINATED_NORMAL)
            {
                return S_OK;
            }
            else
            {
                //Note:  Don't overwrite the failure point we recorded
                //  before, since it may have been changed by some
                //  other thread.
                
                //Don't need to take the critical section here, since
                //we don't care about the current failure point.
                ULONG ulFailurePointCurrent;
                pfi->GetFailureInfo(&ulWaterMark,
                                     &ulFailurePointCurrent);

                // all the data is available now
                if (ulWaterMark >= ulFailurePoint)
                {
                    //We don't care what the return value is, so send
                    //STG_S_BLOCK and all sinks will have fOwner == FALSE
                    _pdacp->NotifySinks(ulWaterMark,
                                        ulFailurePoint,
                                        fAccurate,
                                        STG_S_BLOCK);
                    break;
                }
            }
        }
    }
    
    if ((sc == STG_S_RETRYNOW) ||
        (sc == STG_S_BLOCK) ||
        (sc == STG_S_MONITORING))
    {
        return S_OK;
    }
    else return sc;
}

//+---------------------------------------------------------------------------
//
//  Member:	CAsyncConnection::GetConnectionInterface, public
//
//  Synopsis:	
//
//  Arguments:	
//
//  Returns:	Appropriate status code
//
//  Modifies:	
//
//  History:	30-Dec-95	SusiA	Created
//
//  Notes:	
//
//----------------------------------------------------------------------------

STDMETHODIMP CAsyncConnection::GetConnectionInterface(IID *pIID)
{
    olDebugOut((DEB_ITRACE,
                "In  CAsyncConnection::GetConnectionInterface:%p()\n",
                this));

    
    *pIID = IID_IProgressNotify;
          
    olDebugOut((DEB_ITRACE, "Out CAsyncConnection::GetConnectionInterface\n"));
    return S_OK;
}



//+---------------------------------------------------------------------------
//
//  Member:	CAsyncConnection::GetConnectionPointContainer, public
//
//  Synopsis:	
//
//  Arguments:	
//
//  Returns:	Appropriate status code
//
//  Modifies:	
//
//  History:	30-Dec-95	SusiA	Created
//
//  Notes:	
//
//----------------------------------------------------------------------------

STDMETHODIMP CAsyncConnection::GetConnectionPointContainer(
    IConnectionPointContainer ** ppCPC)
{
    olDebugOut((DEB_ITRACE,
                "In  CAsyncConnection::GetConnectionPointContainer:%p()\n",
                this));

    *ppCPC = _pCPC;
    _pCPC->AddRef();
    
    olDebugOut((DEB_ITRACE,
                "Out CAsyncConnection::GetConnectionPointContainer\n"));
    return S_OK;
}

//+---------------------------------------------------------------------------
//
//  Member:	CAsyncConnection::EnumConnections, public
//
//  Synopsis:	
//
//  Arguments:	
//
//  Returns:	Appropriate status code
//
//  Modifies:	
//
//  History:	30-Dec-95	SusiA	Created
//
//  Notes:	
//
//----------------------------------------------------------------------------

STDMETHODIMP CAsyncConnection::EnumConnections(
    IEnumConnections **ppEnum)
{
    olDebugOut((DEB_ITRACE, "In  CAsyncConnection::EnumConnections:%p()\n", this));
    olDebugOut((DEB_ITRACE, "Out CAsyncConnection::EnumConnections\n"));
    return E_NOTIMPL;
}

//+---------------------------------------------------------------------------
//
//  Member:	CAsyncConnection:: Advise, public
//
//  Synopsis:	
//
//  Arguments:	
//
//  Returns:	Appropriate status code
//
//  Modifies:	
//
//  History:	29-Dec-95	SusiA	Created
//
//  Notes:	
//
//----------------------------------------------------------------------------

STDMETHODIMP CAsyncConnection::Advise(IUnknown *pUnkSink,
                                      DWORD *pdwCookie)
{
    SCODE sc;
    IProgressNotify *ppnSink;
    
    olDebugOut((DEB_ITRACE, "In  CAsyncConnection::Advise:%p()\n", this));

    olChk(pUnkSink->QueryInterface(IID_IProgressNotify, (void **)&ppnSink));
    sc = _pdacp->AddConnection(ppnSink, pdwCookie);
    ppnSink->Release();
    
    olDebugOut((DEB_ITRACE, "Out CAsyncConnection::Advise\n"));
EH_Err:
    return sc;
}


//+---------------------------------------------------------------------------
//
//  Member:	CAsyncConnection::Unadvise, public
//
//  Synopsis:	
//
//  Arguments:	
//
//  Returns:	Appropriate status code
//
//  Modifies:	
//
//  History:	30-Dec-95	SusiA	Created
//
//  Notes:	
//
//----------------------------------------------------------------------------

STDMETHODIMP CAsyncConnection::Unadvise(DWORD dwCookie)
{
    SCODE sc;
    olDebugOut((DEB_ITRACE, "In  CAsyncConnection::Unadvise:%p()\n", this));
    sc = _pdacp->RemoveConnection(dwCookie);
    olDebugOut((DEB_ITRACE, "Out CAsyncConnection::Unadvise\n"));
    return sc;
}

//+---------------------------------------------------------------------------
//
//  Member:	CAsyncConnectionContainer::EnumConnectionPoints, public
//
//  Synopsis:	Return enumerator on connection points
//
//  Arguments:	[ppEnum] -- Return pointer of enumerator
//
//  Returns:	Appropriate status code
//
//  History:	28-Dec-95	SusiA	Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CAsyncConnectionContainer::EnumConnectionPoints(
    IEnumConnectionPoints **ppEnum)
{
    olDebugOut((DEB_ITRACE,
                "In  CAsyncConnectionContainer::EnumConnectionPoints:%p()\n",
                this));
    olDebugOut((DEB_ITRACE,
                "Out CAsyncConnectionContainer::EnumConnectionPoints\n"));
    return E_NOTIMPL;
}


//+---------------------------------------------------------------------------
//
//  Member:	CAsyncConnectionContainer::FindConnectionPoint, public
//
//  Synopsis:	Return a connection point given an IID
//
//  Arguments:	[iid] -- IID to return connection point for
//              [ppCP] -- Return location for pointer
//
//  Returns:	Appropriate status code
//
//  History:	28-Dec-95	SusiA	Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CAsyncConnectionContainer::FindConnectionPoint(
    REFIID iid,
    IConnectionPoint **ppCP)
{
    olDebugOut((DEB_ITRACE,
                "In  CAsyncConnectionContainer::FindConnectionPoint:%p()\n",
                this));

    CAsyncConnection *pcp;
    
    if (IsEqualIID(iid, IID_IProgressNotify))
    {
        pcp = &_cpoint;
    }
    else
    {
        *ppCP = NULL;
        return E_NOINTERFACE;
    }

    pcp->AddRef();
    *ppCP = pcp;
    
    olDebugOut((DEB_ITRACE,
                "Out CAsyncConnectionContainer::FindConnectionPoint\n"));
    return S_OK;
}


//+---------------------------------------------------------------------------
//
//  Member:	CAsyncConnectionContainer::InitConnection, public
//
//  Synopsis:	
//
//  Arguments:	
//
//  Returns:	Appropriate status code
//
//  History:	10-Apr-96	PhilipLa	Created
//
//  Notes:	
//
//----------------------------------------------------------------------------

SCODE CAsyncConnectionContainer::InitConnection(CAsyncConnection *pacParent)
{
    return _cpoint.Init(this, pacParent);
}


//+---------------------------------------------------------------------------
//
//  Member:	CAsyncConnectionContainer::InitClone, public
//
//  Synopsis:	
//
//  Arguments:	
//
//  Returns:	Appropriate status code
//
//  History:	10-Apr-96	PhilipLa	Created
//
//  Notes:	
//
//----------------------------------------------------------------------------

SCODE CAsyncConnectionContainer::InitClone(CAsyncConnection *pac)
{
    return _cpoint.InitClone(this, pac);
}