/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2007/X/X - 2015/02/17
/// 
/// @brief  WinSock 関連
/// 
/// ****************************************************************************

#pragma once

#ifdef _MSC_VER
  #pragma comment(lib, "ws2_32.lib")
  #pragma comment(lib, "wininet.lib")
#endif

//#ifdef CPL_DEBUG
//# pragma comment(lib, "zlib_d.lib")
//#else
//# pragma comment(lib, "zlib_r.lib")
//#endif

#include <winsock2.h>
#include <wininet.h>
#include <ws2tcpip.h>


#include "../Base/CPL_WIN_Base.h"
#include "../Base/CPL_WIN_Thread.h"


namespace CPL {
namespace WIN {

const int SOCKET_RETURN_CODE_CLOSE   = -2;
const int SOCKET_RETURN_CODE_TIMEOUT = -3;
const int SOCKET_RETURN_CODE_EXIT    = -4;

// _____________________________________________________________________________
/// @brief エラーメッセージの出力とエラーコードの取得（WinSock用？）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DWORD OutputWSALastErrorMessage();


// _____________________________________________________________________________
/// @brief イベントオブジェクト
///
// *****************************************************************************
class WSEvent
{
  WSAEVENT m_event;
public:
  WSEvent()
  {
    m_event = ::WSACreateEvent();
  }
  //WSEvent( WSEvent const& e ) :
  //  m_event( e.GetHandle() )
  //{}
  WSEvent( WSAEVENT evt )
    : m_event( evt )
  {}
  ~WSEvent()
  {
    ::WSACloseEvent( m_event );
  }

  WSAEVENT GetHandle() const { return m_event; }

  void SetEvent(){ ::SetEvent( m_event ); }
  void ResetEvent(){ ::ResetEvent( m_event ); }
};


// _____________________________________________________________________________
/// @brief イベント関連付けオブジェクト
///
// *****************************************************************************
struct WSEventSelect
{
  WSEvent m_evt_sock;
  SOCKET  m_socket;
  //DbgMsg  dm__;

  WSEventSelect( SOCKET in_sock, long in_flag, int& out_result )
    : m_socket( in_sock )/*,
    dm__( CPL_CLASSNAME )*/
  {
    out_result = WSAEventSelect( m_socket, m_evt_sock.GetHandle(), in_flag );
    if( out_result == SOCKET_ERROR )
    {
      m_socket = 0;
    }
  }
  ~WSEventSelect()
  {
    if( m_socket )
    {
      WSAEventSelect( m_socket, m_evt_sock.GetHandle(), 0 ); // リセット（ハンドルは無視される）
    }
  }

  WSAEVENT  GetEvent() const { return m_evt_sock.GetHandle(); }
};


// _____________________________________________________________________________
/// @brief WinSock管理クラス
///
/// ※ ラッパークラスみたいなもんです。
///
// *****************************************************************************
class WinSock {
public:
private:
  friend class WinSock;

  WSAData   m_WSAData;
  HINSTANCE m_hdl_dll_zlib;

public:
  using
    func_type_zlib_compress2 =
      int(__stdcall *)
        ( u_char_ptr  dest
        , u_long_ptr  destLen
        , u_char_cptr source
        , u_long      sourceLen
        , int level
        );
  using
    func_type_zlib_uncompress =
      int(__stdcall *)
        ( u_char_ptr  dest
        , u_long_ptr  destLen
        , u_char_cptr source
        , u_long      sourceLen
        );

  CPL_SINGLETON_INIT( func_type_zlib_compress2,  zlib_compress2,  nullptr )
  CPL_SINGLETON_INIT( func_type_zlib_uncompress, zlib_uncompress, nullptr )

  //static LPHOSTENT
  //    GetHostByName( const char* name );

  //static LPHOSTENT
  //    GetHostByAddr
  //      ( const char FAR* addr
  //      , int             len
  //      , int             type
  //      );

  static int
      GetHostName( char* name, int namelen );

  //static char*
  //    Inet_NtoA( in_addr in_ );

  //static int
  //    GetAddrInfo(
  //      addrinfo& out_addrinfo,
  //      char_cptr in_hostname,
  //      char_cptr in_service,
  //      addrinfo* in_ptr_hints = nullptr );

  static int
      SetSockOption
        ( SOCKET      in_socket
        , int         in_level
        , int         in_optname
        , const char* in_optval
        , int         in_optlen
        );

  const WSAData*
      GetWSAData() const;

  WinSock
  ( _in<int     > in_major_ver
  , _in<int     > in_minor_ver
  , pin<str_type> in_path_zlib_dll
  );
  ~WinSock();
};





// _____________________________________________________________________________
/// @brief WinInetクラス
///
// *****************************************************************************
class Inet {
//  ２．InternetOpen関数でInternet APIを初期化する
//３．InternetConnect関数でFTPセッションハンドルを取得する
//４．FtpSetCurrentDirectory関数で現在のFTPのディレクトリをセットする
//５．FtpGetFile関数でファイルをダウンロードする
//６．InternetCloseHandle関数でFTPとインターネットをクローズする

  HINTERNET m_hInet;

public:
  Inet( LPCTSTR  in_agent,        // アプリケーションの名前
        DWORD    in_access_type,  // アクセスタイプ
        LPCTSTR  in_proxy_name,   // プロキシサーバーの名前
        LPCTSTR  in_proxy_bypass, // ホストの名前またはIPアドレスのオプショナルリストのアドレス
        IN DWORD in_flags )
  {
    m_hInet =
      InternetOpen(
        in_agent,
        in_access_type,
        in_proxy_name,
        in_proxy_bypass, in_flags );
  }

  HINTERNET GetInternetHandle() const { return m_hInet; }
};


} // namespace WIN
} // namespace CPL
