// Copyright (C) 1996-1997 Microsoft Corporation. All rights reserved.

// contains internet helper classes CDownloadSink and CInternetControl

#include "header.h"
#include "Internet.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...

// REVIEW: 06-Jul-1997	[ralphw] Why do we need this for HHCtrl?

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)
{
	m_control = control;
	m_control->AddRef();

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

	DBWIN("CDownloadSink created.");
}

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_NOCOPYDATA;
	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)", grfBSCF, dwSize,
			pFmtetc ? "pFmtetc" : "NULL", pstgmed ? "pstgmed" : "NULL");
	DBWIN(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)
{
	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)
{
#if 0
	11-Jun-1997    [ralphw] m_host->CreateMoniker generates a compiler
	error. Doesn't exist in any header file that I can find.

	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 );
#else
	return ::CreateURLMoniker(0,url,ppmkr);
#endif
}

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)) {

		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::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);
}
