/**
@file download.hpp
@brief Windows用ダウンロードするためのロジック

*/

#ifndef DKUTIL_NETWORK_WIN32_DOWNLOAD_HPP
#define DKUTIL_NETWORK_WIN32_DOWNLOAD_HPP

#include <Urlmon.h>
#include <helper/windows.h>
#include <dkutil/dll.hpp>


namespace dkutil{



/**

ダウンロードクラス。
IDownload::URLDownloadToFileだけでもダウンロードは出来る。


モジュールとして完成させるには、
このクラスを継承してガチャガチャMFCバリに定義して使う
具体的には
<PRE>
	STDMETHODIMP_(ULONG) AddRef()	{ return (++m_cRef); }
	STDMETHODIMP_(ULONG) Release()	{ return (m_cRef ? --m_cRef : 0L); }
	STDMETHODIMP GetBindInfo( DWORD*, BINDINFO* ) { return E_NOTIMPL; }
	STDMETHODIMP GetPriority( LONG* ) { return E_NOTIMPL; }
	STDMETHODIMP OnLowResource( DWORD ) { return E_NOTIMPL; }
	STDMETHODIMP OnDataAvailable( DWORD, DWORD, FORMATETC*, STGMEDIUM* ) { return E_NOTIMPL; }
	STDMETHODIMP OnObjectAvailable( REFIID, IUnknown* ) { return E_NOTIMPL; }
	STDMETHODIMP OnStartBinding( DWORD, IBinding* ) { return E_NOTIMPL; }
	STDMETHODIMP OnStopBinding( HRESULT, LPCWSTR ) { return E_NOTIMPL; }
	STDMETHODIMP QueryInterface( REFIID riid, void** ppv )
	STDMETHODIMP OnProgress( ULONG cur, ULONG max, ULONG status, LPCWSTR txt )
</PRE>
らを定義する。
詳しくは[google:caldix]のソースが参考になる。

*/
class IDownload : public IBindStatusCallback{	
public:
	/*typedef HRESULT (*URLDownloadToFile_type)(
		LPUNKNOWN pCaller,
    LPCTSTR szURL,
    LPCTSTR szFileName,
    DWORD dwReserved,
    LPBINDSTATUSCALLBACK lpfnCB
	);*/
	typedef HRESULT  (WINAPI *URLDownloadToFile_type)(
		LPUNKNOWN pCaller,
    LPCTSTR szURL,
    LPCTSTR szFileName,
    DWORD dwReserved,
    LPBINDSTATUSCALLBACK lpfnCB
	);
	virtual ~IDownload()
	{
	}


	static bool URLDownloadToFile(parm_string URL,parm_string local_filename,
		IBindStatusCallback *onProgressP=NULL)
	{
		DLLManager mgr("urlmon.dll");
		
		URLDownloadToFile_type pf = 
#ifndef UNICODE
			(URLDownloadToFile_type)mgr.find("URLDownloadToFileA");
#else
			(URLDownloadToFile_type)mgr.find("URLDownloadToFileW");
#endif
		if(NULL==pf){
			return false;
		}
		return S_OK == pf(NULL,URL.c_str(),local_filename.c_str(),0,onProgressP);
	}

	bool download(parm_string URL,parm_string local_filename)
	{
		return URLDownloadToFile(URL,local_filename,this);
	}
	/*

	STDMETHODIMP OnProgress( 
		ULONG ulProgress,
    ULONG ulProgressMax,
    ULONG ulStatusCode,
    LPCWSTR szStatusText
		)
	{


	}*/

	/*
	template<class FUNCTOR>
	bool download(
	*/
};

///@note caldixのソースを元に作ってみた。
class CDownload : public IDownload
{
public:
	CDownload() : 
		m_cRef( 1 ) , m_stateCancel(false),
		mPrevTime(0) , mCur(0) , mMax(0),mStatus(0)
	{}

private:
	//const kiArray<DLLInfo*>& m_lst;
	//const kiPath& m_pth;
	unsigned long m_cRef;
	/*
	unsigned long m_total;
	unsigned long m_step;
	unsigned long m_startTime, m_lastTime;
	*/
	bool m_stateCancel;

	DWORD mPrevTime;
	ULONG mCur;
	ULONG mMax;
	ULONG mStatus;
public:


	STDMETHODIMP_(ULONG) AddRef()	{ return (++m_cRef); }
	STDMETHODIMP_(ULONG) Release()	{ return (m_cRef ? --m_cRef : 0L); }
	STDMETHODIMP GetBindInfo( DWORD*, BINDINFO* ) { return E_NOTIMPL; }
	STDMETHODIMP GetPriority( LONG* ) { return E_NOTIMPL; }
	STDMETHODIMP OnLowResource( DWORD ) { return E_NOTIMPL; }
	STDMETHODIMP OnDataAvailable( DWORD, DWORD, FORMATETC*, STGMEDIUM* ) { return E_NOTIMPL; }
	STDMETHODIMP OnObjectAvailable( REFIID, IUnknown* ) { return E_NOTIMPL; }
	STDMETHODIMP OnStartBinding( DWORD, IBinding* ) { return E_NOTIMPL; }
	STDMETHODIMP OnStopBinding( HRESULT, LPCWSTR ) { return E_NOTIMPL; }
	
	STDMETHODIMP QueryInterface( REFIID riid, void** ppv )
	{
		*ppv = NULL;
		AddRef();
		if( IsEqualIID( riid, IID_IUnknown ) || IsEqualIID( riid, IID_IBindStatusCallback ) )
			*ppv = (IBindStatusCallback*)this;
		else
		{
			Release();
			return E_NOINTERFACE;
		}
		return NOERROR;
	}
	void notifyCancel(){
		m_stateCancel = true;
	}
	///経過時間を取得する
	DWORD getProgressTime()const{
		return ::GetTickCount() - mPrevTime;
	}
	ULONG getProgress()const{
		return mCur;
	}
	ULONG getProgressMax()const{
		return mMax;
		}
	///@return CDownload::OnProgress()を見てください
	ULONG getStatusCode()const{
		return mStatus;
		}
	

	/**
	@note
	http://msdn.microsoft.com/workshop/networking/moniker/reference/enums/bindstatus.asp
	2005/03/06 http://www.microsoft.com/japan/msdn/library/ja/vclib/html/_mfc_CAsyncMonikerFile.3a3a.OnProgress.asp?frame=true
	より引用
	
	BINDSTATUS_FINDINGRESOURCE 
	バインド操作は、オブジェクトを持っているリソースまたはバインドされるストレージを検索中です。szStatusText には、検索されるリソースの表示名 (たとえば "www.microsoft.com") を指定します。 
	BINDSTATUS_CONNECTING 
	バインド操作は、バインドされるオブジェクトまたはストレージがあるリソースに接続中です。szStatusText には、接続されるリソースの表示名 (たとえば IP アドレス) を指定します。 
	BINDSTATUS_SENDINGREQUEST 
	バインド操作は、バインドされるオブジェクトまたはストレージを要求中です。szStatusText には、オブジェクトの表示名 (たとえばファイル名) を指定します。 
	BINDSTATUS_REDIRECTING 
	バインド操作は、別のデータの場所にリダイレクトされました。szStatusText には、新しいデータの場所の表示名を指定します。 
	BINDSTATUS_USINGCACHEDCOPY 
	バインド操作は、要求されたオブジェクトまたはストレージをキャッシュされたコピーから取得中です。szStatusText は NULL です。 
	BINDSTATUS_BEGINDOWNLOADDATA 
	バインド操作は、バインドされるオブジェクトまたはストレージの受信を開始しました。szStatusText には、データの場所の表示名を指定します。 
	BINDSTATUS_DOWNLOADINGDATA 
	バインド操作は、バインドされるオブジェクトまたはストレージの受信を継続しています。szStatusText には、データの場所の表示名を指定します。 
	BINDSTATUS_ENDDOWNLOADDATA 
	バインド操作は、バインドされるオブジェクトまたはストレージの受信を終了しました。szStatusText には、データの場所の表示名を指定します。 
	BINDSTATUS_CLASSIDAVAILABLE 
	バインドされるオブジェクトのインスタンスを作成中です。szStatusText には、新しいオブジェクトの CLSID を文字列形式で指定し、クライアントはバインド操作を必要に応じてキャンセルできます。 
	
	*/
	STDMETHODIMP OnProgress( ULONG cur, ULONG max, ULONG status, LPCWSTR txt )
	{
		//msgLoop( PEEK );

		if( m_stateCancel || status == BINDSTATUS_ENDDOWNLOADDATA)
		{
			m_stateCancel = false;
			return E_ABORT;
		}
		mCur = cur;
		mMax = max;
		mStatus = status;


		mPrevTime = ::GetTickCount();

		//sendMsgToItem( IDC_BAR, PBM_SETPOS, (int)(((double)(m_step+cur))/m_total*32768) );
		return S_OK;
	}
};

}//end of dkutil namespace



#endif