//=--------------------------------------------------------------------------=
// Internet.Cpp
//=--------------------------------------------------------------------------=
// Copyright 1995-1997 Microsoft Corporation.  All Rights Reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
// PARTICULAR PURPOSE.
//=--------------------------------------------------------------------------=
//
// contains internet helper classes CDownloadSink and CInternetControl
//
#include "IPServer.H"
#include "Internet.H"
#include "Util.H"

static VARTYPE rgI4[] = { VT_I4 };

typedef enum {
    InternetEvent_Progress = 0,
    InternetEvent_ReadyStateChange = 1
} INTERNETEVENTS;

static EVENTINFO rgEvents [] = {
    { DISPID_PROGRESS, 1, rgI4 },           // (long percentDone)
    { DISPID_READYSTATECHANGE, 1, rgI4 },       // (OLE_READYSTATE newState)
};


// local class for doing async monitoring. It's not really all that
// general purpose, but it does the job...


class CDownloadSink : public IBindStatusCallback
{
public:
    CDownloadSink(IUnknown *punkOuter,CInternetControl *,DISPID );
    ~CDownloadSink();

    STDMETHOD(QueryInterface)(REFIID riid, void **ppvObjOut);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();

        STDMETHOD(OnStartBinding)(
            /* [in] */ DWORD grfBSCOption,
            /* [in] */ IBinding *pib);

        STDMETHOD(GetPriority)(
            /* [out] */ LONG *pnPriority);

        STDMETHOD(OnLowResource)(
            /* [in] */ DWORD reserved);

        STDMETHOD(OnProgress)(
            /* [in] */ ULONG ulProgress,
            /* [in] */ ULONG ulProgressMax,
            /* [in] */ ULONG ulStatusCode,
            /* [in] */ LPCWSTR szStatusText);

        STDMETHOD(OnStopBinding)(
            /* [in] */ HRESULT hresult,
            /* [in] */ LPCWSTR szError);

        STDMETHOD(GetBindInfo)(
            /* [out] */ DWORD *grfBINDINFOF,
            /* [unique][out][in] */ BINDINFO *pbindinfo);

        STDMETHOD(OnDataAvailable)(
            /* [in] */ DWORD grfBSCF,
            /* [in] */ DWORD dwSize,
            /* [in] */ FORMATETC *pformatetc,
            /* [in] */ STGMEDIUM *pstgmed);

        STDMETHOD(OnObjectAvailable)(
            /* [in] */ REFIID riid,
            /* [iid_is][in] */ IUnknown *punk);



        CDownloadSink * Next()                  { return(m_next); }
        void            Next(CDownloadSink *n)  { m_next = n; }

        DISPID          DispId()  { return(m_propId); }
        IBinding *      Binding() { return(m_binding); }

private:

        CDownloadSink *         m_next;
        CInternetControl *      m_control;
        DISPID                  m_propId;
        IBinding *              m_binding;
        DWORD                   m_ref;
                IStream *                               m_stream;

};


CDownloadSink::CDownloadSink
(
        IUnknown *              punkOuter,
        CInternetControl *      control,
        DISPID                  propId
)
{
//      CHECK_POINTER(control);

        m_control = control;
        m_control->AddRef();

        m_propId  = propId;
        m_next    = 0;
        m_binding = 0;
        m_ref     = 0;
        m_stream  = 0;
}

CDownloadSink::~CDownloadSink()
{
        if( m_control )
                m_control->Release();
        if( m_binding )
                m_binding->Release();
        if( m_stream )
                m_stream->Release();
}

STDMETHODIMP
CDownloadSink::QueryInterface(const GUID &iid,void **ppv )
{
        if( IsEqualGUID(iid,IID_IUnknown) || IsEqualGUID(iid,IID_IBindStatusCallback) )
        {
                *ppv = this;
                AddRef();
                return(NOERROR);
        }
        return( E_NOINTERFACE );
}

STDMETHODIMP_(ULONG)
CDownloadSink::AddRef()
{
        return(++m_ref);
}

STDMETHODIMP_(ULONG)
CDownloadSink::Release()
{
        if(!--m_ref)
        {
                delete this;
                return(0);
        }
        return( m_ref );
}


STDMETHODIMP
CDownloadSink::GetBindInfo( DWORD *grfBINDF, BINDINFO *pbindInfo)
{
    *grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
        return(NOERROR);
}


STDMETHODIMP
CDownloadSink::OnStartBinding(DWORD /*grfBSCOption*/,IBinding *pib)
{
        // BUGBUG: should check to see options are what we think they are
        m_binding = pib;
        pib->AddRef();
        return(NOERROR);
}


STDMETHODIMP
CDownloadSink::GetPriority( LONG *pnPriority)
{
        return(E_NOTIMPL);
}

STDMETHODIMP
CDownloadSink::OnProgress
(
     ULONG ulProgress,
     ULONG ulProgressMax,
     ULONG ulStatusCode,
     LPCWSTR pwzStatusText
)
{
        return(m_control->OnProgress(m_propId,ulProgress,
                                                        ulProgressMax,ulStatusCode,pwzStatusText) );
}

STDMETHODIMP
CDownloadSink::OnDataAvailable
(
     DWORD                      grfBSCF,
     DWORD                      dwSize,
     FORMATETC *        pFmtetc,
         STGMEDIUM *    pstgmed
)
{
#ifdef DEBUG
        char msg[200];
        wsprintf(msg,"::OnDataAvailable(%0xd,%d,%s,%s)\n",grfBSCF,dwSize,
                pFmtetc ? "pFmtetc" : "NULL", pstgmed ? "pstgmed" : "NULL" );
        OutputDebugString(msg);
#endif

    if( !m_stream )
                m_stream = pstgmed->pstm;

        return(m_control->OnData(       m_propId,
                                                                grfBSCF,
                                                                m_stream,
                                                                dwSize ));
}

STDMETHODIMP
CDownloadSink::OnObjectAvailable
(
        REFIID riid,
    IUnknown *punk
)
{
        return(E_NOTIMPL);
}


STDMETHODIMP
CDownloadSink::OnLowResource( DWORD reserved)
{
        // BUGBUG: really should have this kind of harsh policy on this ...
        m_binding->Abort();
        return(S_OK);
}

STDMETHODIMP
CDownloadSink::OnStopBinding(HRESULT hrError, LPCWSTR szError)
{
        m_binding->Release();
        m_binding = 0;
        m_control->Release();
        m_control = 0;

        return(NOERROR);
}



//------------------------------------------------------
//
// class CInternetControl
//
//
CInternetControl::CInternetControl
(
        IUnknown *      pUnkOuter,
        int                     iPrimaryDispatch,
        void *          pMainInterface
)
        : COleControl(pUnkOuter,iPrimaryDispatch,pMainInterface)
{
        m_host = 0;
        m_readyState = READYSTATE_LOADING;
}

CInternetControl::~CInternetControl()
{
        if( m_host )
                m_host->Release();
}


HRESULT CInternetControl::InternalQueryInterface
(
    REFIID  riid,
    void  **ppvObjOut
)
{
    *ppvObjOut = NULL;
     return COleControl::InternalQueryInterface(riid, ppvObjOut);
}



HRESULT
CInternetControl::GetBindHost()
{

        if( m_host )
                return(NOERROR);

    // Try service provider first...

        IServiceProvider * serviceProvider = 0;

        HRESULT hr = m_pClientSite->QueryInterface
                                                                        (
                                                                                IID_IServiceProvider,
                                                                                (void**)&serviceProvider
                                                                        );

        if( SUCCEEDED(hr) )
    {
                hr = serviceProvider->QueryService
                                    (
                                        SID_IBindHost,
                                        IID_IBindHost,
                                        (void**)&m_host
                                    );
                serviceProvider->Release();
    }

    if( FAILED(hr) )
    {
        // Some containers put IBindHost directly on the client site

        hr = m_pClientSite->QueryInterface
                                                                        (
                                        IID_IBindHost,
                                        (void**)&m_host
                                                                        );


    }

        return(hr);

}


HRESULT CInternetControl::GetAMoniker( LPOLESTR url, IMoniker ** ppmkr )
{
        HRESULT hr = GetBindHost();

        if( SUCCEEDED(hr) )
                hr = m_host->CreateMoniker(url,NULL, ppmkr,0);

        if( FAILED(hr) )
    {
           // FUTURE: This really should be a call to MkParseDisplayNameEx!!!
       hr = ::CreateURLMoniker(0,url,ppmkr);
       // hr = ::MkParseDisplayNameEx(0, url, 0, ppmkr);
    }

        return( hr );
}


HRESULT CInternetControl::SetupDownload( LPOLESTR url, DISPID propId )
{
        CHECK_POINTER(url);

        IMoniker * pmkr;

        HRESULT hr = GetAMoniker( url, &pmkr );

        IBindCtx * pBindCtx = 0;

        if( SUCCEEDED(hr) )
    {
        hr = ::CreateBindCtx(0,&pBindCtx);
    }

        CDownloadSink * sink = 0;

        if( SUCCEEDED(hr) )
        {
                sink = new CDownloadSink(0,this,propId);
                if( sink )
                        sink->AddRef();
        }

        if( SUCCEEDED(hr) && !sink )
                hr = E_OUTOFMEMORY;

        if( SUCCEEDED(hr) )
        {
                // BUGBUG: There should be a define for 0x77
                hr = ::RegisterBindStatusCallback(pBindCtx, sink,0, 0) ;
        }

        IStream * strm = 0;

        if( SUCCEEDED(hr) )
                hr = pmkr->BindToStorage( pBindCtx, 0, IID_IStream, (void**)&strm );

        if( strm )
                strm->Release();

        if( pBindCtx )
                pBindCtx->Release();

        if( FAILED(hr) && sink )
                sink->Release();

        return(hr);

}


HRESULT CInternetControl::OnData( DISPID, DWORD,IStream *, DWORD)
{
        return(NOERROR);
}

HRESULT CInternetControl::OnProgress( DISPID, ULONG progress, ULONG themax, ULONG, LPCWSTR)
{
        return(NOERROR);
}


HRESULT CInternetControl::FireReadyStateChange( long newState )
{
        FireEvent( &::rgEvents[InternetEvent_ReadyStateChange], m_readyState = newState );
        return(S_OK);
}

HRESULT CInternetControl::FireProgress( ULONG dwAmount )
{
        FireEvent( &::rgEvents[InternetEvent_Progress], dwAmount );
        return(S_OK);
}
