
#ifndef DKUTIL_NETWORK_SOCKET_HPP
#define DKUTIL_NETWORK_SOCKET_HPP

#include <gimite/socket.h>
#include <dkutil/detail/stream_interface.hpp>
#include <dkutil/detail/lunalibdef.hpp>
#include <dkutil/boost/parm_string.hpp>
#include <list>
#include <dkutil/filesystem/big_filestream.hpp>

/*
#include "Luna.h"
#include "LunaSocket.h"
#include "LunaSystem.h"
#include "LunaMemory.h"

#include "LunaWork.h"
*/
namespace dkutil{

///@see gimite::startup_socket()
inline bool init_socket(){
	return gimite::startup_socket();
}
///@see gimite::cleanup_socket()
inline void end_socket(){
	gimite::cleanup_socket();
}


/**
	ネットワークバイトオーダーをホストバイトオーダーに変換

	@param pData	[in/out] 変換するデータの格納先
	@param Size		[in] データのバイト数
*/

inline void NetToHost( void *pData, long Size )
{
	switch ( Size )
	{
	case 2:
		*((unsigned short *)pData) = ::ntohs( *((unsigned short *)pData) );
		break;
	case 4:
		*((unsigned long *)pData) = ::ntohl( *((unsigned long *)pData) );
		break;
	}
}


/**
	エラーコードからエラー文字列を取得します

	@param ErrorCode	[in] エラーコード
	@param pErrStr		[out] エラーメッセージ格納先
*/

inline void GetSocketError( long ErrorCode, char *pErrStr )
{
	switch ( ErrorCode )
	{
	case WSABASEERR:				strcpy( pErrStr, "エラーは発生していない" );								break;
	case WSAEINTR:					strcpy( pErrStr, "関数呼び出しが中断された" );								break;
	case WSAEBADF:					strcpy( pErrStr, "無効なファイルハンドル" );								break;
	case WSAEACCES:					strcpy( pErrStr, "アクセスが拒否された" );									break;
	case WSAEFAULT:					strcpy( pErrStr, "無効なバッファアドレス" );								break;
	case WSAEINVAL:					strcpy( pErrStr, "無効な引数が渡された" );									break;
	case WSAEMFILE:					strcpy( pErrStr, "使用中のソケットの数が多すぎる" );						break;
	case WSAEWOULDBLOCK:			strcpy( pErrStr, "操作はブロッキングされる" );								break;
	case WSAEINPROGRESS:			strcpy( pErrStr, "すでにブロッキング手続きが実行されている" );				break;
	case WSAEALREADY:				strcpy( pErrStr, "要求された操作は既に実行中、または実行済み" );			break;
	case WSAENOTSOCK:				strcpy( pErrStr, "指定されたソケットが無効である" );						break;
	case WSAEDESTADDRREQ:			strcpy( pErrStr, "操作の実行に送信先アドレスが必要" );						break;
	case WSAEMSGSIZE:				strcpy( pErrStr, "メッセージサイズが大きすぎる" );							break;
	case WSAEPROTOTYPE:				strcpy( pErrStr, "ソケットは要求されたプロトコルに適合していない" );		break;
	case WSAENOPROTOOPT:			strcpy( pErrStr, "不正なプロトコルオプション" );							break;
	case WSAEPROTONOSUPPORT:		strcpy( pErrStr, "プロトコルがサポートされていない" );						break;
	case WSAESOCKTNOSUPPORT:		strcpy( pErrStr, "指定されたソケットタイプはサポートされていない" );		break;
	case WSAEOPNOTSUPP:				strcpy( pErrStr, "要求された操作はサポートされていない" );					break;
	case WSAEPFNOSUPPORT:			strcpy( pErrStr, "プロトコルファミリがサポートされていない" );				break;
	case WSAEAFNOSUPPORT:			strcpy( pErrStr, "アドレスファミリがサポートされていない" );				break;
	case WSAEADDRINUSE:				strcpy( pErrStr, "アドレスは既に使用中である" );							break;
	case WSAEADDRNOTAVAIL:			strcpy( pErrStr, "無効なネットワークアドレス" );							break;
	case WSAENETDOWN:				strcpy( pErrStr, "ネットワークがダウンしている" );							break;
	case WSAENETUNREACH:			strcpy( pErrStr, "指定されたネットワークホストに到達できない" );			break;
	case WSAENETRESET:				strcpy( pErrStr, "ネットワーク接続が破棄された" );							break;
	case WSAECONNABORTED:			strcpy( pErrStr, "ネットワーク接続が破棄された" );							break;
	case WSAECONNRESET:				strcpy( pErrStr, "ネットワーク接続が相手によって破棄された" );				break;
	case WSAENOBUFS:				strcpy( pErrStr, "バッファが不足している" );								break;
	case WSAEISCONN:				strcpy( pErrStr, "ソケットは既に接続されている" );							break;
	case WSAENOTCONN:				strcpy( pErrStr, "ソケットは接続されていない" );							break;
	case WSAESHUTDOWN:				strcpy( pErrStr, "ソケットはシャットダウンされている" );					break;
	case WSAETOOMANYREFS:			strcpy( pErrStr, "参照の数が多すぎる" );									break;
	case WSAETIMEDOUT:				strcpy( pErrStr, "接続要求がタイムアウトした" );							break;
	case WSAECONNREFUSED:			strcpy( pErrStr, "接続が拒否された" );										break;
	case WSAELOOP:					strcpy( pErrStr, "ループ" );												break;
	case WSAENAMETOOLONG:			strcpy( pErrStr, "名前が長すぎる" );										break;
	case WSAEHOSTDOWN:				strcpy( pErrStr, "ホストがダウンしている" );								break;
	case WSAEHOSTUNREACH:			strcpy( pErrStr, "ホストへの経路がない" );									break;
	case WSAENOTEMPTY:				strcpy( pErrStr, "ディレクトリが空ではない" );								break;
	case WSAEPROCLIM:				strcpy( pErrStr, "プロセスの数が多すぎる" );								break;
	case WSAEUSERS:					strcpy( pErrStr, "ユーザの数が多すぎる" );									break;
	case WSAEDQUOT:					strcpy( pErrStr, "ディスククォータ" );										break;
	case WSAESTALE:					strcpy( pErrStr, "実行しようとした操作は廃止されている" );					break;
	case WSAEREMOTE:				strcpy( pErrStr, "リモート" );												break;
	case WSASYSNOTREADY:			strcpy( pErrStr, "ネットワークサブシステムが利用できない" );				break;
	case WSAVERNOTSUPPORTED:		strcpy( pErrStr, "Winsock.dllのバージョンが範囲外である" );					break;
	case WSANOTINITIALISED:			strcpy( pErrStr, "WinSockシステムが初期化されていない" );					break;
	case WSAEDISCON:				strcpy( pErrStr, "シャットダウン処理中" );									break;
	case WSAENOMORE:				strcpy( pErrStr, "データはこれ以上存在しない" );							break;
	case WSAECANCELLED:				strcpy( pErrStr, "操作は取り消された" );									break;
	case WSAEINVALIDPROCTABLE:		strcpy( pErrStr, "サービスプロバイダの関数テーブルが無効" );				break;
	case WSAEINVALIDPROVIDER:		strcpy( pErrStr, "サービスプロバイダが無効" );								break;
	case WSAEPROVIDERFAILEDINIT:	strcpy( pErrStr, "サービスプロバイダの初期化に失敗した" );					break;
	case WSASYSCALLFAILURE:			strcpy( pErrStr, "システムコールに失敗した" );								break;
	case WSASERVICE_NOT_FOUND:		strcpy( pErrStr, "サービスが見つからない" );								break;
	case WSATYPE_NOT_FOUND:			strcpy( pErrStr, "タイプが見つからない" );									break;
	case WSA_E_CANCELLED:			strcpy( pErrStr, "検索がキャンセルされた" );								break;
	case WSAEREFUSED:				strcpy( pErrStr, "操作は拒否された" );										break;
	case WSAHOST_NOT_FOUND:			strcpy( pErrStr, "ホストが見つからない" );									break;
	case WSATRY_AGAIN:				strcpy( pErrStr, "指定されたホストが見つからない、またはサービスの異常" );	break;
	case WSANO_RECOVERY:			strcpy( pErrStr, "回復不能なエラーが発生した" );							break;
	case WSANO_DATA:				strcpy( pErrStr, "要求されたタイプのデータレコードが見つからない" );		break;
	}
}
/**
@brief		ソケットクラス
@author		Noriyuki Lee
@since		2003.07.01
@note
LunaライブラリのLunaSocketクラスを元に、
Lunaから切り離したライブラリです。
*/
class Socket{
public:
	/**
		@brief	ソケット用データ

		WinSockを管理するために必要な情報を<BR>
		格納している構造体です
	*/
	struct WORK_LUNASOCKET
	{
		char LastError[1024];							///< エラー格納用文字列
		bool IsInitialize;								///< 初期化完了フラグ
		HOSTINFO HostInfo;								///< ホスト情報
		HOSTENT HostEntry;								///< ホストエントリー
		SOCKET Socket;									///< 自分のソケット
		bool IsConnect;									///< 接続中か否か
	};
	// VARIABLE
private:
	WORK_LUNASOCKET *pWork;
public:
	Socket(){

		pWork = (WORK_LUNASOCKET *)malloc(sizeof(WORK_LUNASOCKET));
		if(pWork){
			Initialize(pWork);
		}
	}
	~Socket(){
		Uninitialize();
		if(pWork){
			free(pWork);
		}
	}
	void print(const char *p){
		printf(p);
	}
	void nextline(){
		printf("\n");
	}
	/**
		クラスの初期化をします。

		@param pWorkData	[in] ワークエリアのアドレス

		@retval true	成功
		@retval false	失敗
	*/
	bool Initialize( void *pWorkData )
	{
		print( "LunaSocketクラスの初期化" );
		nextline();

		//----------------------------------------------------------
		// ワークエリアの取得
		//----------------------------------------------------------
		pWork = (WORK_LUNASOCKET*)pWorkData;

		//----------------------------------------------------------
		// ワークエリアの初期化
		//----------------------------------------------------------
		MemoryClear( pWork, sizeof(WORK_LUNASOCKET) );
		pWork->Socket = INVALID_SOCKET;

		return true;
	}


	/**
		クラスの解放をします。
	*/

	void Uninitialize( void )
	{
		print( "LunaSocketクラスの解放" );
		nextline();

		Shutdown();
	}


	/**
		接続中のネットワークから切断します
	*/

	void Shutdown( void )
	{
		if(!pWork)
			return;
		//-------------------------------------------------------
		// ソケット切断
		//-------------------------------------------------------
		if ( pWork->IsConnect )
		{
			char TempBuff[64] = "";
			::shutdown( pWork->Socket, SD_SEND );
			while ( ::recv( pWork->Socket, TempBuff, sizeof(TempBuff), 0 ) > 0 );
			::shutdown( pWork->Socket, SD_BOTH );
			::closesocket( pWork->Socket );

			pWork->IsConnect = false;
			pWork->IsInitialize = false;
		}
	}


	/*
		最後に発生したエラーを文字列の状態で取得します

		@param pError	: エラー文字列格納先バッファ

		@retval true	エラーがある場合
		@retval false	エラーがない場合
	*/

	bool GetLastError( char *pError )
	{
		if ( pWork->LastError[0] != '\0' )
		{
			strcpy( pError, pWork->LastError );
			return true;
		}

		return false;
	}


	/**
		WinSockの初期化をします

		@retval true	成功
		@retval false	失敗
	*/

	bool Start( void )
	{
		pWork->Socket = ::socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
		if ( pWork->Socket == INVALID_SOCKET )
		{
			GetMakeError( ::WSAGetLastError(), pWork->LastError );
			return false;
		}

		pWork->IsInitialize = true;

		return true;
	}


	/**
		指定名のホスト情報を取得します

		@param pServerString	[in] ホスト識別名称格納先アドレス（ホスト名でもＩＰでも可）
		@param pHost			[out] ホスト情報格納先アドレス

		@retval true	成功
		@retval false	失敗
	*/

	bool GetHostInfo( const char *pServerString, HOSTINFO *pHost )
	{
		long i;
		IN_ADDR InAddrHost;
		HOSTENT *pHostEntry;

		//------------------------------------------------------------
		// 指定された文字列がホスト名かＩＰアドレスか調べる
		//------------------------------------------------------------
		InAddrHost.s_addr = ::inet_addr( pServerString );
		if ( InAddrHost.s_addr == INADDR_NONE )
		{
			// ＩＰではないのでホスト名として処理
			pHostEntry = ::gethostbyname( pServerString );
			if ( pHostEntry == NULL )
			{
				GetMakeError( ::WSAGetLastError(), pWork->LastError );
				return false;
			}
		}
		else
		{
			// ＩＰアドレスのようでふ
			pHostEntry = ::gethostbyaddr( (const char *)&InAddrHost, sizeof(IN_ADDR), AF_INET );
			if ( pHostEntry == NULL )
			{
				GetMakeError( ::WSAGetLastError(), pWork->LastError );
				return false;
			}
		}

		//------------------------------------------------------------
		// ホスト情報の保存
		//------------------------------------------------------------
		MemoryClear( &pWork->HostInfo, sizeof(pWork->HostInfo) );

		// ホスト公式名
		strcpy( pWork->HostInfo.Name, pHostEntry->h_name );

		// ホストの別名
		for ( i = 0; pHostEntry->h_aliases[i] != NULL; i++ )
		{
			strcpy( pWork->HostInfo.Alias[pWork->HostInfo.AliasCount++], pHostEntry->h_aliases[i] );
		}

		// アドレス
		for ( i = 0; pHostEntry->h_addr_list[i] != NULL; i++ )
		{
			const char *p = ::inet_ntoa( *((IN_ADDR*)pHostEntry->h_addr_list[i]));
			strcpy( pWork->HostInfo.Address[pWork->HostInfo.AddressCount++], p );
		}

		// データサイズ
		pWork->HostInfo.Length = pHostEntry->h_length;
		// データタイプ
		pWork->HostInfo.Type = pHostEntry->h_addrtype;

		//------------------------------------------------------------
		// ホストデータコピー
		//------------------------------------------------------------
		if ( pHost != NULL )
		{
			MemoryCopy( pHost, &pWork->HostInfo, sizeof(HOSTINFO) );
		}

		MemoryCopy( &pWork->HostEntry, pHostEntry, sizeof(HOSTENT) );

		return true;
	}

	/**
		ローカルホスト名を取得します

		@param pName	[out] ホスト名格納先ポインタ
	*/

	void GetLocalHostName( char *pName )
	{
		char Buff[256] = "";
		// ホスト名取得
		::gethostname( Buff, sizeof(Buff) );
		strcpy( pName, Buff );
	}

	
	/**
		指定したサーバーへの接続処理を行います

		@param pServerString	[in] ホスト識別名称格納先アドレス（ホスト名でもＩＰでも可）
		@param PortNo			[in] ポート番号

		@retval true	成功
		@retval false	失敗
	*/
	
	bool ConnectServer( const char *pServerString, unsigned short PortNo )
	{
		long ReturnCode;
		SOCKADDR_IN SockAddr;

		//------------------------------------------------------------
		// ホストデータ取得
		//------------------------------------------------------------
		if ( !GetHostInfo( pServerString, NULL ) )
		{
			return false;
		}

		//------------------------------------------------------------
		// 接続用データ生成
		//------------------------------------------------------------
		MemoryClear( &SockAddr, sizeof(SOCKADDR_IN) );
		SockAddr.sin_family	= AF_INET;
		SockAddr.sin_port	= ::htons( PortNo );
		SockAddr.sin_addr	= *((IN_ADDR*)pWork->HostEntry.h_addr_list[0]);

		//------------------------------------------------------------
		// 接続
		//------------------------------------------------------------
		ReturnCode = ::connect( pWork->Socket, (SOCKADDR*)&SockAddr, sizeof(SOCKADDR_IN) );
		if ( ReturnCode == SOCKET_ERROR )
		{
			ReturnCode = ::WSAGetLastError();
			if ( ReturnCode != WSAEWOULDBLOCK )
			{
				GetMakeError( ::WSAGetLastError(), pWork->LastError );
				return false;
			}
		}

		pWork->IsConnect = true;

		return true;
	}

	
	/**
		指定したデータを接続先に送信します

		@param pData	[in] データアドレス
		@param Size		[in] データサイズ

		@retval -2		接続は閉じられた
		@retval -1		送信に失敗
		@retval 0以上	送信したバイト数
	*/
	
	long Send( const void *pData, unsigned long Size )
	{
		const char *pBuffer = (const char *)pData;
		unsigned long Rest = Size;

		//----------------------------------------------------
		// データ送信用ループ
		//----------------------------------------------------
		while ( Rest > 0 )
		{
			//--------------------------------------------
			// 残りのバイト数を送信する
			//--------------------------------------------
			unsigned long SendSize = ::send( pWork->Socket, pBuffer, Rest, 0 );
			if ( SendSize == SOCKET_ERROR )
			{
				GetMakeError( ::WSAGetLastError(), pWork->LastError );
				return -1;
			}
			else
			if ( SendSize == 0 )
			{
				strcpy( pWork->LastError, "回線は切断されました" );
				return -2;
			}

			// 送信した分進める
			Rest -= SendSize;
			pBuffer += SendSize;
		}

		return (Size - Rest);
	}

	
	/**
		指定したデータを接続先から受信します

		@param pData	[in] データアドレス
		@param Size		[in] データサイズ

		@retval -2		接続は閉じられた
		@retval -1		送信に失敗
		@retval 0以上	受信したバイト数
	*/
	
	long Receive( void *pData, unsigned long Size )
	{
		char *pBuffer = (char *)pData;
		unsigned long Rest = Size;

		//----------------------------------------------------
		// データ受信用ループ
		//----------------------------------------------------
		while ( Rest > 0 )
		{
			//--------------------------------------------
			// 残りのバイト数を受信する
			//--------------------------------------------
			unsigned long ReceiveSize = ::recv( pWork->Socket, pBuffer, Rest, 0 );
			if ( ReceiveSize == SOCKET_ERROR )
			{
				GetMakeError( ::WSAGetLastError(), pWork->LastError );
				return -1;
			}
			else
			if ( ReceiveSize == 0 )
			{
				strcpy( pWork->LastError, "回線は切断されました" );
				return -2;
			}

			// 受信した分進める
			Rest -= ReceiveSize;
			pBuffer += ReceiveSize;
		}

		return (Size - Rest);
	}

	
	/**
		HTTPサーバーからファイルを取得します<BR>
		※システムコールバックあり。

		@param pServerName		[in] サーバー名
		@param pFileNameList[]	[in] ファイル名リスト
		@param pDataList[]		[out] データ格納先
		@param DataSizeList		[out] データサイズ格納先
		@param FileCount		[in] ファイル数

		@retval true		成功
		@retval false		失敗
	*/
	
	bool GetFileFromHTTP( const char *pServerName, const char *pFileNameList[], void *pDataList[], unsigned long DataSizeList[], long FileCount )
	{
		const unsigned long BUFFER_SIZE = 4 * 1024;
		char *pBuffer = (char *)MemoryAlloc( BUFFER_SIZE );
		if ( pBuffer == NULL )
		{
			strcpy( pWork->LastError, "作業用メモリが確保できませんでした" );
		}
		else
		{
			long Result;
			bool IsInit = pWork->IsInitialize;

			//------------------------------------------------------------------
			// WinSockの初期化
			//------------------------------------------------------------------
			if ( IsInit )
			{
				if ( !Start() )
				{
					MemoryFree( pBuffer );
					return false;
				}
			}
		
			//------------------------------------------------------------------
			// 接続
			//------------------------------------------------------------------
			if ( !ConnectServer( pServerName, 80 ) )
			{
				MemoryFree( pBuffer );
				return false;
			}

			//------------------------------------------------------------------
			// ファイル受信処理
			//------------------------------------------------------------------
			for ( long i = 0; i < FileCount; i++ )
			{
				pDataList[i] = MemoryAlloc( 0 );
				DataSizeList[i] = 0;
				// ファイル受信リクエストを送信
				sprintf( pBuffer, "GET %s\n", pFileNameList[i] );
				Result = ::send( pWork->Socket, pBuffer, strlen(pBuffer), 0 );
				if ( Result == SOCKET_ERROR ) break;

				while ( true )
				{
					Result = ::recv( pWork->Socket, pBuffer, BUFFER_SIZE, 0 );
					if ( Result == SOCKET_ERROR )
					{
						MemoryFree( pDataList[i] );
						DataSizeList[i] = 0;
						break;
					}
					else
					if ( Result == 0 )
					{
						break;
					}
					else
					{
						// メモリ再割り当て
						void *pNew = MemoryReAlloc( pDataList[i], DataSizeList[i] + Result );
						if ( pNew == NULL )
						{
							DataSizeList[i] = 0;
							MemoryFree( pDataList[i] );
							strcpy( pWork->LastError, "作業用メモリが確保できませんでした" );
							break;
						}

						pDataList[i] = pNew;
						MemoryCopy( (unsigned char*)pDataList[i] + DataSizeList[i], pBuffer, Result );

						DataSizeList[i] += Result;
					}
				}
			}

			//------------------------------------------------------------------
			// 解放処理
			//------------------------------------------------------------------
			if ( IsInit )
			{
				Shutdown();
			}

			MemoryFree( pBuffer );
		}

		return true;
	}

	
	/**
		エラーコードからエラー文字列を取得します

		@param ErrorCode	[in] エラーコード
		@param pErrStr		[out] エラーメッセージ格納先
	*/
	
	void GetMakeError( long ErrorCode, char *pErrStr )
	{
		MemoryClear( pWork->LastError, sizeof(pWork->LastError) );
		GetSocketError( ErrorCode, pErrStr );
	}

};//end of class
/**
@note
d金魚が各ソケットライブラリを参考にして作ったライブラリです。
*/
class CEasySocket{
	
  SOCKET sd;
	BOOST_STATIC_CONSTANT(int,lasterror_str_length = 1024);
	char mLastError[lasterror_str_length + 1];
public:
#ifdef WIN32
	BOOST_STATIC_CONSTANT(SOCKET,invalid_socket = INVALID_SOCKET);
#else
	BOOST_STATIC_CONSTANT(SOCKET,invalid_socket = (SOCKET)(~0));
#endif
		//invalid_socket = -1
	
	CEasySocket(){
		//DKUTIL_STRUCTURE_INIT(sd);
		sd = invalid_socket;
		memset(mLastError,0,sizeof(mLastError));
	}
	~CEasySocket(){
		disconnect();
	}
	bool good()const{
		return sd != invalid_socket;
	}
	std::string getLastError()const{
		char copyed[lasterror_str_length + 1];
		memcpy(copyed,mLastError,lasterror_str_length);
		copyed[lasterror_str_length ] = '\0';
		std::string str = mLastError;
		return str;
	}

	void disconnect(){
		if(sd != invalid_socket){
      shutdown_output();
      shutdown_input();
      closesocket(sd);
			sd = invalid_socket;
    }
	}
	/*bool recv(char *buff,int size){
		 if(sd == invalid_socket){
        return false;
    }else{
			int ret = ::recv(sd, (char *)buff, size, 0);
      if(ret <= 0){
          return false;
      }else{
         
      }
    }
		return true;
	}*/
		/**
		指定したデータを接続先から受信します

		@param pData	[in] データアドレス
		@param Size		[in] データサイズ

		@retval -2		接続は閉じられた
		@retval -1		送信に失敗
		@retval 0			成功
	*/
	
	long responsible_recv( void *pData, unsigned long Size ,size_t *readsize)
	{
		char *pBuffer = (char *)pData;
		unsigned long Rest = Size;

		//----------------------------------------------------
		// データ受信用ループ
		//----------------------------------------------------
		while ( Rest > 0 )
		{
			//--------------------------------------------
			// 残りのバイト数を受信する
			//--------------------------------------------
			unsigned long ReceiveSize = ::recv( sd, pBuffer, Rest, 0 );
			if ( ReceiveSize == SOCKET_ERROR )
			{
				GetMakeError( ::WSAGetLastError(), mLastError );
				return -1;
			}
			else
			if ( ReceiveSize == 0 )
			{
				strcpy( mLastError, "回線は切断されました" );
				if(Size == Rest){
					return -2;
				}else{
					goto End;
				}
			}

			// 受信した分進める
			Rest -= ReceiveSize;
			pBuffer += ReceiveSize;
		}
	End:
		*readsize = (Size - Rest);
		return 0;
	}

	void GetMakeError( long ErrorCode, char *pErrStr )
	{
		MemoryClear( mLastError, sizeof(mLastError) );
		GetSocketError( ErrorCode, pErrStr );
	}
	void MemoryLastError(){
		GetMakeError( ::WSAGetLastError(),mLastError);
	}
	/**
		指定したデータを接続先に送信します

		@param pData	[in] データアドレス
		@param Size		[in] データサイズ

		@retval -2		接続は閉じられた
		@retval -1		送信に失敗
		@retval 0以上	送信したバイト数
		@note
		ほぼ確実にSize分のデータを送信します。
	*/
	int responsible_send(const void *pData,size_t Size){
		if(sd==invalid_socket){
			return -1;
		}
		const char *pBuffer = (const char *)pData;
		unsigned long Rest = Size;

		//----------------------------------------------------
		// データ送信用ループ
		//----------------------------------------------------
		while ( Rest > 0 )
		{
			//--------------------------------------------
			// 残りのバイト数を送信する
			//--------------------------------------------
			unsigned long SendSize = ::send( sd, pBuffer, Rest, 0 );
			if ( SendSize == SOCKET_ERROR )
			{
				GetMakeError( ::WSAGetLastError(),mLastError);
				return -1;
			}
			else if ( SendSize == 0 )
			{
				strcpy( mLastError, "回線は切断されました" );
				return -2;
			}

			// 送信した分進める
			Rest -= SendSize;
			pBuffer += SendSize;
		}

		return (Size - Rest);

	}
	void shutdown_input(){
		if(sd != invalid_socket){
        shutdown(sd, SHUT_RD);
    }
	}
  void shutdown_output(){
		if(sd != invalid_socket){
        shutdown(sd, SHUT_WR);
    }
	}
	bool connect(const char* address, unsigned short port)
	{
		sockaddr_in sa;
		memset(&sa, 0, sizeof(sa));

		// 名前解決
		unsigned long addr = inet_addr(address);
		if(addr != INADDR_NONE){
				sa.sin_family = AF_INET;
				sa.sin_addr.s_addr = addr;
				sa.sin_port = htons(port);
		}else{
				hostent* hp = gethostbyname(address);
				if(hp){
						sa.sin_family = AF_INET;
						sa.sin_addr.s_addr = *reinterpret_cast<unsigned long*>(hp->h_addr);
						sa.sin_port = htons(port);
				}else{
						return false;
				}
		}
		// ソケット作成
		sd = socket(AF_INET, SOCK_STREAM, 0);
		if(sd == invalid_socket){
				return false;
		}
		// 接続
		if(::connect(sd, reinterpret_cast<sockaddr*>(&sa), sizeof(sockaddr)) != 0){
				// 失敗
				closesocket(sd);
				sd = invalid_socket;
				return false;
		}
		return true;
	}


	struct HTTPDataType{
		HTTPDataType( parm_string get , parm_string filename ) : 
			mAccessValid(false) , mFileSize(0) ,
			mGET(get) ,mFilename(filename) , 
			mProcessValid(false)
		{
		
		}
		///内部処理で成功したかどうか
		bool mProcessValid;
		///URLが有効かどうか
		bool mAccessValid;
		///GET /hoge/hoge.html HTTP/1.0みたいな奴の　/hoge/hoge.htmlの部分
		std::string mGET;
		///書き出す予定のファイル名
		std::string mFilename;
		///アクセスに使用したリクエスト
		std::string mAccessRequest;
		///ファイルサイズ
		ULONGLONG mFileSize;
	};
	typedef std::list<HTTPDataType> filelist_t;

	/**
	getDataHTTP()等で使用するHTTP接続をして流れてきたデータを処理するファンクター
	*/
	struct HTTPfile_functor{
		big_filestream mfs;
		///@return falseで失敗
		bool operator()(const HTTPDataType &dt,int state,
			const BYTE *data,size_t size,void *pointer)
		{
			bool r = false;
			try{
				switch(state){
				case http_begin:
					if(dt.mFilename.empty()){
						return false;
					}
					r = mfs.reset(dt.mFilename,write_mode | binary_mode);
					/*if(false==r){
						return false;
					}*/
					r = mfs.open();
					//break;
				case http_loading:
					mfs.responsible_write(data,size);
					r = true;
					break;
				case http_end:
					mfs.responsible_write(data,size);
					mfs.close();
					r = true;
					break;
				case http_error:
					mfs.close();
					break;
				default:
					dkcmFORCE_NOT_ASSERT("HTTPfile_functor 状態遷移エラー");
				}
			}catch(filesystem_error er){
				DEBUGMB(er.what());
				return false;
			}
			return r;
		}
	};

	bool getDataHTTP(parm_string ServerName,int port, 
		filelist_t &flist)
	{
		HTTPfile_functor func;
		return getDataHTTPEx(ServerName,port,flist,func);
	}
	/*
	static std::string DefaultRequestStr
		="GET %s HTTP/1.0\r\n \
			User-Agent:Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.7) Gecko/20040803 Firefox/0.9.3 \
			\r\n\r\n";
	*/

	/**
	@param ServerName[in] サーバー名
	@param port[in] 80がデファクトスタンダード 8080とかもある。
	@param flist[in][out] あらかじめ定義したダウンロードリスト
	@param functor__[in] ファンクター
	@param pointer[in] 何かへのポインタ
	@param tempsize[in] テンポラリバッファのサイズ デフォルトで256KB
	@note

	ServerName( サーバー名指定) に http://www.google.co.jp/hogehoge/index.html のようにURLを渡してはいけない。
	しっかりしたIPアドレスか、文字列の名前（ www.google.co.jp 等)を渡すようにする。

	また、	functor__　には HTTPfile_functor のような感じのものを使う
	@see HTTPfile_functor
	*/
	enum{
		///新たに始まる
		http_begin = 0,
		///只今、読み込み中
		http_loading,
		///上手く終了しました。
		http_end,
		///エラーなので、このデータは使わないで下さい。
		http_error,
	};

	

	template<class FUNCTOR__>
	bool getDataHTTPEx( 
		parm_string ServerName,int port, 
		filelist_t &flist,FUNCTOR__ functor__ ,
		void *pointer = NULL,
		parm_string request = "GET %s HTTP/1.0\r\n \
			User-Agent:Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.7) Gecko/20040803 Firefox/0.9.3 \
			\r\n\r\n",
		size_t tempsize=1024 * 256)
	{
		bool r=false;
		if(false==connect(ServerName,port))
		{//繋ぐのに失敗
			MemoryLastError();
			return false;
		}
		if(good()==false){
			MemoryLastError();
			return false;
		}

		
		std::string tempstr;
		filelist_t::iterator it = flist.begin();



		scoped_buffer_byte tempbuff(tempsize);
		if(false==tempbuff.isValid()){
			strcpy(mLastError,"メモリ確保エラー");
			goto End;
		}

		int tr;
		tr = -1;

		for(;it != flist.end();it++)
		{
			tempstr.clear();


			
			//リクエスト 送信データを用意
			
			/*
			tempstr += "GET ";
			tempstr += (*it).mGET;
			tempstr += " HTTP/1.0";
			tempstr += "\r\n\r\n";
			*/
      
			// HTTPリクエスト送信
      /*
			sockout.write("GET " + url +" HTTP/1.1\r\n");
      sockout.write("Host: " + host + "\r\n");
      sockout.write("Connection: close\r\n");
      sockout.write("\r\n");
			*/
			tempstr += "GET " + (*it).mGET + " HTTP/1.1\r\n";
      tempstr += "Host: ";
			tempstr += ServerName.c_str();
			tempstr += "\r\n";
      tempstr += "Connection: close\r\n";
      tempstr += "\r\n";

			(*it).mAccessRequest = tempstr;

			if((*it).mGET.empty())
			{//ちと、やばいよ。これじゃぁエラーだよ。
				continue;
			}
			
			//送りつける
			tr = responsible_send(tempstr.c_str(),tempstr.size());

			if(tr < 0){
				goto End;
			}
			//読みこんだサイズを保存
			size_t readsize;
			//tempbuff.size()のtemp
			size_t tempsize = tempbuff.size();
			//recvがエラーしたらTRUE
			bool recv_error = false;
			//recvの戻り値
			int recv_res;
			//ステート 前のステート
			int state,back_state;
			
			back_state = state = -1;
			
			for(;;){
				//データをゲット
				recv_res = responsible_recv(tempbuff.get(),tempsize,&readsize);
				
				//recv_error = (recv_res < 0);
				if(-1 != state){
					back_state = state;
				}
				//状態を仕分ける
				switch(recv_res){
				case -2:
					state = http_end;
					break;
				case -1:
					state = http_error;
					break;
				default:
					if(-1==state ){
						state = http_begin;
					}else{
						state = http_loading;
					}
				}//end of switch

				//後はファンクター任せ (recv_res再利用)
				recv_res = functor__( (*it) , state , tempbuff.get(), readsize, pointer );
				
				if(recv_res==false)
				{//ファンクターがエラーになった。やばい！終了しなきゃ。
					(*it).mProcessValid = false;
					(*it).mAccessValid = (state != http_error);
					break;

				}

				if(http_end==state){//大成功!!!
					(*it).mAccessValid = true;
					(*it).mProcessValid = true;
					break;
				}	
				if( http_error==state)
				{//失敗!!!
					if(-1==back_state){
						(*it).mAccessValid = false;
					}else{
						(*it).mAccessValid = true;
					}
					(*it).mProcessValid = false;
					break;
				}
			}//end of recv for

		}//end of for
	
	End:
		if(it==flist.end()){
			r = true;
		}
		disconnect();

		return r;
	}


};



}//end of dkutil namespace



#endif