/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2007/X/X - 2015/11/12
/// 
/// @brief  WinSock サーバー。
/// 
/// ****************************************************************************

#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include "CPL_WIN_WS_Server.h"

namespace CPL {
namespace WIN {


/// @cond
// _____________________________________________________________________________
// @brief サーバークラスの初期化。
//
// @param[in] in_max_connect
//   : 同時最大接続数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Server::zzf_Init_( int in_max_connect )
{
  try {
    //m_sp_server_socket = new WIN::SocketTCP( "localhost", Core::StringA(in_port_No) );
    //m_sp_server_socket = new WIN::SocketTCP();

    //sockaddr_in server;
    //memset( &server, 0, sizeof(server) );
    //server.sin_family = AF_INET;
    //server.sin_port = htons(in_port_No);
    //server.sin_addr.S_un.S_addr = INADDR_ANY;
    //m_sp_server_socket->SetSockAddr( (sockaddr*)&server, sizeof(server) );

    if( Bind() == SOCKET_ERROR )
    {
      int err_code = WIN::OutputWSALastErrorMessage();
      CPL_THROW_CODE(
        err_code,
        "CPL::WIN::Server::Bind()に失敗\n" );
    }

    if( Listen( in_max_connect ) == SOCKET_ERROR )
    {
      int err_code = WIN::OutputWSALastErrorMessage();
      CPL_THROW_CODE(
        err_code,
        "CPL::WIN::Server::Listen()に失敗\n" );
    }
  }
  //catch( Core::Exception e )
  //{
  //  Close();
  //  throw e;
  //}
  catch(...)
  {
    Close();
    throw;
  }
}
/// @endcond



// _____________________________________________________________________________
/// クローズイベント。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Server::On_Closed( cref<SpSocket> in_sp_socket )
{
  if( in_sp_socket.get() != this )
  {
    auto itr_sp_socket = m_socket_list.begin();
    while( itr_sp_socket != m_socket_list.end()  &&  *itr_sp_socket )
    {
      if( *itr_sp_socket == in_sp_socket )
      {
        m_socket_list.erase( itr_sp_socket );
        break;
      }

      ++itr_sp_socket;
    }
  }

  SocketBase::On_Closed( in_sp_socket );
}



// _____________________________________________________________________________
/// @brief ソケットとポートの連結
///
/// @retval 0            : 正常終了
/// @retval SOCKET_ERROR : エラー
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int Server::Bind()
{
  sockaddr* addr = GetSockAddr();
  int length = static_cast<int>(GetAddrLength());
  int ret = ::bind( m_socket, addr, length );

  return ret;
}

// _____________________________________________________________________________
/// @brief ユーザーの接続待機（listenモード）
///
/// @param[in] in_max_connect
///   : 同時最大接続数。
///
/// @retval 0            : 正常終了
/// @retval SOCKET_ERROR : エラー
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int Server::Listen( _in<int> in_max_connect )
{
  m_max_connect = in_max_connect;

  int ret = listen( m_socket, in_max_connect );

  //if( ret != SOCKET_ERROR )
  //{
//    this->m_socket_table.Clear();
//    this->m_socket_table.Resize( in_max_connect );
  //}

  return ret;
}


// _____________________________________________________________________________
/// @brief クローズ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int Server::Close()
{
  if( m_socket == INVALID_SOCKET )
  {
    return 0;
  }

  AutoThreadLock_SockList locking;
  
  auto itr_sp_socket = m_socket_list.begin();
  while( itr_sp_socket != m_socket_list.end()  &&  *itr_sp_socket )
  {
    (*itr_sp_socket)->ExitOn();
    (*itr_sp_socket)->Close();

    ++itr_sp_socket;
  }
  //
  m_socket_list.clear();

  return SocketBase::Close();
}


// _____________________________________________________________________________
/// @brief 指定したソケットIDと一致するソケットポインタを取得。
///
/// @param[in] in_socket_ID
///   : 取得するソケットID
///
/// @return 該当するポインタ。見つからなければnullptrを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SpSocket Server::GetSocket( size_t in_socket_ID ) const
{
  auto itr_sp_socket = m_socket_list.begin();
  while( itr_sp_socket != m_socket_list.end()  &&  *itr_sp_socket )
  {
    if( **itr_sp_socket == (SOCKET)in_socket_ID )
    {
      return *itr_sp_socket;
    }

    ++itr_sp_socket;
  }

  return nullptr;
}



// _____________________________________________________________________________
/// @brief クライアントの接続受け入れ。
///
/// @return
/// SocketTCPのスマートポインタ（SocketTCP::SpLinked）を返す。
/// エラーの場合、nullptrを返す。
///
/// @par エラーの場合例外（Core::ExceptionCode）を投げる。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SpSocket Server::Accept()
{
  //DbgMsg db( CPL_FUNCNAME );
  //size_t new_idx = zzf_NewSocket_(); // ソケット保持用の新しい要素インデックスを取得
  //if( FAILED_INDEX(new_idx) )
  //{
  //  return nullptr; // ソケットが作成できない場合、無効ポインタを返す。
  //}

  // イベント待機。
  EventParam ep;
  ep.in_event_flags = FD_ACCEPT | FD_CLOSE;
  int flag = WaitEvents( ep );
  // エラーチェック
  if( flag == SOCKET_ERROR )
  {
    DWORD err = WIN::OutputWSALastErrorMessage();
    CPL_THROW_CODE( err, "CPL::WIN::Server::Accept() : WaitEvents() is error.\n" );
  }
  // イベントチェックc
  if( ep.out_event_flags == FD_ACCEPT ) // accept要求
  {
    char buf_sockaddr_client[ 128 ] = { 0 };
    int  addr_len = static_cast<int>(this->GetAddrLength());
    addr_len = Min<int>( addr_len, sizeof(buf_sockaddr_client) );
    // accept処理
    SOCKET sock_client =
      ::accept( m_socket,
                (LPSOCKADDR)buf_sockaddr_client,
                &addr_len );
    if( sock_client == INVALID_SOCKET )
    {
      DWORD err = WIN::OutputWSALastErrorMessage();
      CPL_THROW_CODE( err, "CPL::WIN::Server::Accept() : accept() is error.\n" );
    }
    else
    {
      // ソケットをソケットクラスに関連付けて作成。
      SpSocket sp_socket =
        CPL_MAKE_SHARED
          ( SocketTCP
          , sock_client
          , (sockaddr*)&buf_sockaddr_client
          , addr_len
          , GlobalFindSocket( this )
          );

      //
      sp_socket->SetSendPassword( GetSendPassword() );
      sp_socket->SetRecvPassword( GetRecvPassword() );

      AutoThreadLock_SockList locking;

      // 作成されたソケットクラスをノードに追加。
      m_socket_list.push_back( sp_socket );
      GlobalEntrySocket( sp_socket );

      // ソケット用のスレッドを作成。
      SmartCast<SocketTCP*>(sp_socket)->CreateThreadRecv();

      return sp_socket;
    }
  }
  else
  if( ep.out_event_flags == FD_CLOSE ) // CLOSE
  {
    return nullptr;
  }
  else // 強制終了等
  {
    return nullptr;
  }
}


// _____________________________________________________________________________
/// @brief 受信情報の取得。
///
/// @param[out] out_report_table
///   : 情報取得のためのReportInfoBuffer配列（std::vector）への参照。
///
/// @retval true  : データ有り。
/// @retval false : データ無し。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int Server::GetRecvReport( rout<ReportInfoBufferTable> out_report_table ) const
{
  out_report_table.clear();
  
  AutoThreadLock_SockList locking;

  auto itr_sp_socket = m_socket_list.begin();
  while( itr_sp_socket != m_socket_list.end()  &&  *itr_sp_socket )
  {
    (*itr_sp_socket)->GetRecvReport( out_report_table );

    ++itr_sp_socket;
  }

  return (out_report_table.size() > 0); // 成功
}

// _____________________________________________________________________________
/// @brief 送信情報の取得。
///
/// @param[out] out_report_table
///   : 情報取得のためのReportInfoBuffer配列（std::vector）への参照。
///
/// @retval true  : データ有り。
/// @retval false : データ無し。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int Server::GetSendReport( rout<ReportInfoBufferTable> out_report_table ) const
{
  out_report_table.clear();

  AutoThreadLock_SockList locking;
  
  auto itr_sp_socket = m_socket_list.begin();
  while( itr_sp_socket != m_socket_list.end()  &&  *itr_sp_socket )
  {
    (*itr_sp_socket)->GetSendReport( out_report_table );

    ++itr_sp_socket;
  }

  return (out_report_table.size() > 0); // 成功
}



// _____________________________________________________________________________
/// @brief コンストラクタ
///
/// ソケットの作成とポートとの連結を行い、接続要求を待てる状態にする。
/// エラーが起こった場合は例外(クラスException)を投げる
///
/// @param[in] in_port_No
///   : ポート番号。
/// @param[in] in_max_connect
///   : 同時最大接続数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Server::Server( _in<U16> in_port_No, _in<int> in_max_connect )
  : WIN::SocketBase( "localhost", Core::StringA(in_port_No) )
  , m_max_connect( 0 )
{
  zzf_Init_( in_max_connect );

  // 初期化が済んだので、スレッド開始。
  Start();
}



// _____________________________________________________________________________
/// @brief コンストラクタ（イベントウィンドウ関連付けバージョン）
///
/// ソケットの作成とポートとの連結を行い、接続要求を待てる状態にする。
/// エラーが起こった場合は例外(クラスException)を投げる
///
/// @param[in] in_hwnd
///   : イベントを通知するウィンドウハンドル。
/// @param[in] in_port_No
///   : ポート番号。
/// @param[in] in_max_connect
///   : 同時最大接続数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Server::Server
( _in<HWND> in_hwnd
, _in<U16 > in_port_No
, _in<int > in_max_connect
) : WIN::SocketBase( "localhost", Core::StringA(in_port_No) )
  , m_max_connect( 0 )
{
  zzf_Init_( in_max_connect );

  if( in_hwnd )
  {
    int flag =
      WSAAsyncSelect
        ( m_socket
        , in_hwnd
        , kWM_CPL_SOCKET
        , FD_CONNECT | FD_WRITE | FD_READ | FD_CLOSE
        );
    if( flag == SOCKET_ERROR )
    {
      int err = WIN::OutputWSALastErrorMessage();
      CPL_THROW_CODE( err, "WSAAsyncSelect error" );
    }
  }
}


// _____________________________________________________________________________
/// @brief デストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Server::~Server()
{
  // 
  ExitOn();
}


/// @cond
// _____________________________________________________________________________
/// @brief サーバー用スレッド
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//int Server::Call( void* p )
size_t Server::operator()()
{
  DbgMsg db( CPL_FUNCNAME, this->GetThreadName().c_str() );
  try {
    while( true )
    {
      WIN::SpSocket sp_socket = Accept();
      if( sp_socket == nullptr )
      {
        break;
      }
      {
        // 管理しているクライアントソケットノード（自身以降にリスト化されている）を
        // ソケットが閉じていないかチェックし、閉じられていたら破棄（リストから削除）する。
        AutoThreadLock_SockList locking;

        auto itr_sp_socket = m_socket_list.begin();
        while( itr_sp_socket != m_socket_list.end()  &&  *itr_sp_socket )
        {
          if( **itr_sp_socket == INVALID_SOCKET )
          {
            auto temp_itr = itr_sp_socket;
            ++temp_itr;
            GlobalCancelSocket( *itr_sp_socket );
            m_socket_list.erase( itr_sp_socket );
            itr_sp_socket = temp_itr;
            continue;
          }

          ++itr_sp_socket;
        }
      }
      sp_socket->On_Accepted( sp_socket );
    }
  }
  catch(...)
  {
    Core::String module_name;
    GetModuleName( module_name );
    CPL_OUTPUT(
      SwS("%s: CPL::WIN::Server::Call() : throw exception\n"),
      (str_cptr)module_name );
  }
  
  Core::String module_name;
  GetModuleName( module_name );
  CPL_OUTPUT(
    SwS("%s: Server::Call() normal return"),
    (str_cptr)module_name );

  return 0;
}
/// @endcond


// インスタンス（コンパイルチェック用）
//static Server s(0,1);

} // namespace WIN
} // namespace CPL