/**
@file internet.hpp
@brief インターネットに接続する機能
@note
今の所Win32Only...
Windowsは何でもそろって手便利だなぁ〜
*/

#ifndef DKUTIL_NETWORK_INTERNET_HPP
#define DKUTIL_NETWORK_INTERNET_HPP

#include <helper/windows_header.h>
#include <dkutil/network/tcp.hpp>
#include <dkutil/dll.hpp>
#include <dkutil/detail/stream_interface.hpp>
#include <dkutil/network/download.hpp>

namespace dkutil{
class IInternet : public IStream{
public:
	enum{
		///どっちに決めるかは最初の設定次第
		preconfig_type,
		///直接繋ぐ
		direct_type,
		///Proxyを使用して繋ぐ
		proxy_type,
	};
		
	virtual ~IInternet(){}
	
	
};
#ifdef WIN32
	
	

	
class CWinInet : public IInternet
{
public:
	typedef BOOL (*InternetSetOption_type)(
    IN HINTERNET hInternet,
    IN DWORD dwOption,
    IN LPVOID lpBuffer,
    IN DWORD dwBufferLength
	);
	typedef BOOL (*InternetQueryOption_type)(
    IN HINTERNET hInternet,
    IN DWORD dwOption,
    OUT LPVOID lpBuffer,
    IN OUT LPDWORD lpdwBufferLength
	);


	typedef DWORD (*InternetAttemptConnect_type)(
    IN DWORD dwReserved
	);
	typedef BOOL (*InternetCloseHandle_type)(
    IN HINTERNET hInet
	);
	typedef HINTERNET (*InternetOpen_type)(
    IN LPCSTR lpszAgent,
    IN DWORD dwAccessType,
    IN LPCSTR lpszProxyName,
    IN LPCSTR lpszProxyBypass,
    IN DWORD dwFlags
	);
	typedef BOOL (*InternetWriteFile_type)(
    IN HINTERNET hFile,
    IN LPCVOID lpBuffer,
    IN DWORD dwNumberOfBytesToWrite,
    OUT LPDWORD lpdwNumberOfBytesWritten
	);

	typedef BOOL (*InternetReadFile_type)(
    IN HINTERNET hFile,
    IN LPVOID lpBuffer,
    IN DWORD dwNumberOfBytesToRead,
    OUT LPDWORD lpNumberOfBytesRead
	);


	/**
	Internet Explorerのバージョンを得る。
	@return -1で失敗 0がIE3 それ以降IEのバージョンが上がるたびに戻り値が1づつ上がる (IE4 == 1 IE5 == 2 ... )
	*/
	static int GetIEVersion(){
		INTERNET_VERSION_INFO info;
	  DWORD dwSize;
		int r = -1;
	  dwSize = sizeof(info);
		
		DLLManager mgr("Wininet.dll");
		
		InternetQueryOption_type pf = 
			(InternetQueryOption_type)mgr.find("InternetQueryOption");

		if(NULL==pf){
			return -1;
		}
		
	  if( pf( 0, INTERNET_OPTION_VERSION, &info, &dwSize ) ){
	    if( info.dwMajorVersion == 1 ){
	    	if(info.dwMinorVersion < 0){
	    		r = -1;
	    	}else{
	    		r = info.dwMinorVersion;
	    	}
	      /*
	    	switch( info.dwMinorVersion ){
	        case 0:
	          Edit1->Text = "Internet Explorer 3";
	          break;
	        case 1:
	          Edit1->Text = "Internet Explorer 4";
	          break;
	        case 2:
	          Edit1->Text = "Internet Explorer 5";
	          break;
	        default:
	         	r = -1;
	          break;
	      }
	    	*/
	    	
	    }
	  }
		return r;
	}




private:
	///WinInetハンドル
	HINTERNET mH;
	///DLLマネージャー
	DLLManager mDLL;
	
	///エラーメッセージ
	std::string mLastError[1024];
	
	//各関数へのポインタ
	

	InternetSetOption_type InternetSetOption;
	
	InternetAttemptConnect_type InternetAttemptConnect;

	InternetOpen_type InternetOpen;
	
	InternetCloseHandle_type InternetCloseHandle;

	InternetQueryOption_type InternetQueryOption;

	InternetReadFile_type InternetReadFile;

	InternetWriteFile_type InternetWriteFile;

public:


	CWinInet(const char *dllname="Wininet.dll"){
		mH = NULL;
		if(false==mDLL.reset(dllname)){
			return;
		}
#	define CWININET_LOAD_FUNC(name) \
	name = (name##_type)mDLL.find("##name##")
		
		CWININET_LOAD_FUNC(InternetSetOption);
		CWININET_LOAD_FUNC(InternetAttemptConnect);
		/*
		
		InternetSetOption = 
			(InternetSetOption_type)mDLL.find("InternetSetOption");
		
		InternetAttemptConnect = 
			(InternetAttemptConnect_type)mDLL.find("InternetAttemptConnect");
		
		InternetOpen =
			(InternetOpen_type)mDLL.find("InternetOpen");
		
		InternetCloseHandle = 
			(InternetCloseHandle_type)mDLL.find("InternetCloseHandle");
		
		InternetReadFile = 
			(InternetReadFile_type)mDLL.find("InternetReadFile");

		InternetWriteFile =
			(InternetWriteFile_type)mDLL.find("InternetWriteFile");
		*/

	}
	virtual ~CWinInet(){
		close();
	}
	///@return 使える状態ならtrue
	virtual bool good()const{
		return mDLL.empty()==false;
	}
	virtual bool read(void *buff,size_t size,size_t *readsize)
	{
		DWORD rs;
		BOOL r = InternetReadFile(mH,buff,size,&rs);
		*readsize = rs;
		return TRUE==r;
	}
	virtual bool write(const void *buff,size_t size,size_t *write_size)
	{
		DWORD ws;
		BOOL r = InternetWriteFile(mH,buff,size,&ws);
		*write_size = ws;
		return TRUE==r;
	}
		
	///インターネットに接続を確立（常時接続でない場合）
	bool attemptConnect()
	{//ERROR_SUCCESSだと成功
		return ERROR_SUCCESS==InternetAttemptConnect(0);
	}
	///タイムアウトを設定する
	bool setTimeOut(size_t TimeOut = 5000)
	{
		return TRUE==InternetSetOption( mH, INTERNET_OPTION_RECEIVE_TIMEOUT, &TimeOut, sizeof(TimeOut) );
	}
	///インターネットに繋ぐ
	bool open(const char *userAgent,int option)
	{
		DWORD Opt;
		switch(option){
		case preconfig_type:
			Opt = INTERNET_OPEN_TYPE_PRECONFIG;
			break;
		case direct_type:
			Opt = INTERNET_OPEN_TYPE_DIRECT;
			break;
		case proxy_type:
			Opt = INTERNET_OPEN_TYPE_PROXY;
			break;
		default:
			return false;
		}
		mH = InternetOpen(userAgent,Opt,NULL,NULL,0);
		if(NULL==mH)
		{
			mLastError = GetWin32LastErrorString();
			return false;
		}
		return true;
	}
	///インターネットへの接続を終了
	void close(){
		if(mH){
			InternetCloseHandle( mH );
			mH = NULL;
		}
	}
	
	void download(const char *URL,const char *filename){
		URLDownloadToFile(NULL,URL,filename,0,0);
	}
	//void downloadEx(const char *URL
	
	///エラーした時の情報をゲットする
	const char *errorStr()const{
		return mLastError.c_str();
	}

	
	
};
	
typedef CWinInet CInternet;
	
#else //posix...
	
	
#endif

}//end of dkutil namespace



#endif