/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2007/X/X - 2014/02/18
/// 
/// @brief  ソケット管理クラスを提供。
/// @brief 
///  ・このソケットクラスを用いての受信(Recv)はソケットクラスの送信(Send)のみ対応する。
///  　使用者が独自にソケットクラスのSOCKETを用いて転送しても、ソケットクラスのRecv
///  　では、正しく受け取ることができない。
///  ・現在はTCPのみ対応。
///
/// @brief 
///  転送単位は次の構造とする。
///    転送要求ブロック{
///      フラグメント[N]{}
///    }
///  １つの転送要求では複数のバッファを一度に指定でき、１つの転送ブロック単位とし
///  扱われる。
///  この転送ブロックはフラグメントブロックの集まりとして転送され、このフラグメン
///  トは転送要求時のバッファと１：１となることを保障しなければならない。
///  つまり、転送時に３つのバッファを渡すと、３つのフラグメントとしてデータが転送
///  されることになる。
///  そして、受け取り側ではその区切りを取得できる仕様としなければならない。
/// ****************************************************************************

#pragma once

#include "CPL_WIN_WS_Base.h"


namespace CPL {
namespace WIN {

//const int CPL_WIN_WS_SOCKET_SEGMENT_MAX = 9;
const U32 CPL_WIN_WS_SOCKET_DEF_MTU_SIZE = 1440;
const U32 CPL_WIN_WS_SOCKET_TEMP_BUF_SIZE =
            CPL_WIN_WS_SOCKET_DEF_MTU_SIZE * 4; // このサイズは15bit(32K)を超えないこと。
const U32 CPL_WIN_WS_REQUEST_BLOCK_SIZE_MAX = 1024;

const char CPL_WS_SIGNATURE[] = "CPL_WS_SIGNATURE";


// _____________________________________________________________________________
/// @brief ソケット用インターフェイス
///
// *****************************************************************************
class Socket_i_
  : public Core::Sender
  , public Core::Receiver
{
public:
  // _____________________________________________________________________________
  /// @brief フラグメントヘッダ
  ///
  // *****************************************************************************
  struct FragmentHeader {
    U16 f_compress : 1;
    U16 size       : 15; // ヘッダ(FragmentHeader)を含まないデータサイズ。

    FragmentHeader() : f_compress(false), size(0) {}
  };

  // _____________________________________________________________________________
  /// @brief 転送セグメント単位ヘッダ
  /// ※ １つのセグメントは1つ以上のフラグメントで構成されるものとする。
  ///
  // *****************************************************************************
  struct SegmentBlock {
    U32 m_size;
  };

  // _____________________________________________________________________________
  /// @brief 転送要求単位ヘッダ
  /// 
  /// ※ セグメント情報を含めたサイズがCPL_WIN_WS_REQUEST_BLOCK_SIZE_MAXを超えてはならない。
  ///    また、１つの転送要求は１つ以上のセグメントで構成されるものとする。
  ///
  // *****************************************************************************
  struct RequestInfo {
    char         m_signature[ 16 ]; // 
    U32          m_ID;
    U32          m_size; // ヘッダを含む１転送要求単位の全体のサイズ
    U32          m_segment_num;
    SegmentBlock m_segment[ 1 ];

    static int ComputeSize( int in_segment_num )
    {
      return sizeof(RequestInfo) + sizeof(SegmentBlock) * (in_segment_num-1);
    }
    //
    void InitSignature()
    {
      memcpy( m_signature, CPL_WS_SIGNATURE, sizeof(m_signature) );
    }
    bool IsSignature() const
    {
      return memcmp( m_signature, CPL_WS_SIGNATURE, sizeof(m_signature) ) == 0;
    }
  private:
    RequestInfo(){}
  };


  typedef std::vector< SpByteBuffer >           SpByteBufferTable;
  typedef Core::TypeMappedBuffer< RequestInfo > RequestInfoBuffer;
protected:
  typedef std::queue< U32 >               SizeQueue;
  typedef std::deque< RequestInfoBuffer > RequestQueue;
  //typedef std::queue< SpByteBuffer >      BufferQueue;
  typedef std::list< sdptr<Socket_i_> >   SocketList;

  Socket_i_(){}
  
public:
  virtual ~Socket_i_(){}

  // =============================================================================
  // 送信情報
  struct SendParam {
    U8_cptr       in_buf;           // 転送データが格納されたバッファへのポインタ。
    S64           in_length;        // 上記バッファサイズ。
    SDEF          out_error;        // エラー格納用
    S64 volatile* out_size_sending; // 現在の転送済みサイズ取得用ポインタ

    SendParam
    ( U8_cptr       in_buf_           = nullptr,
      S64           in_length_        = 0,
      S64 volatile* out_size_sending_ = nullptr )
      : in_buf          ( in_buf_ )
      , in_length       ( in_length_ )
      , out_error       ( 0 )
      , out_size_sending( out_size_sending_ )
    {}
    SendParam( SendParam const& sp )
    {
      memcpy( this, &sp, sizeof(*this) );
    }
  };

  // =============================================================================
  // 受信情報
  struct RecvParam {
    char*         out_buf;            // 取得用バッファ。バッファサイズはin_length以上であること。
    U64           in_length;          // 読み込みサイズ。
    U64 volatile* out_size_receiving; // 現在の受信済みサイズ取得用ポインタ
    int           out_error;          // エラーとなった場合、エラーコードが格納される。

    RecvParam
    ( char_ptr      out_buf_            = nullptr
    , U64           in_length_          = 0
    , U64* volatile out_size_receiving_ = nullptr
    ) : out_buf           ( out_buf_ )
      , in_length         ( in_length_ )
      , out_size_receiving( out_size_receiving_ )
      , out_error         ( 0 )
    {}
    RecvParam( RecvParam const& rp )
    {
      memcpy( this, &rp, sizeof(*this) );
    }
  };

  // =============================================================================
  // イベント情報
  struct EventParam {
    WSAEVENT* in_events;        // イベント配列へのポインタ。
    int       in_event_num;     // イベント配列の要素数。
    long      in_event_flags;   // 関連付けるイベントフラグ。
    long      out_event_flags;  // 発生したイベントフラグ。
                                // タイムアウトした場合0、離脱した場合-1となる。
    int       out_event_index;  // 発生したイベントの要素インデックス。
    DWORD     out_error;        // エラーとなった場合、エラーコード（GetLastError()）が格納される。

    EventParam()
      : in_events      ( nullptr )
      , in_event_num   ( 0 )
      , in_event_flags ( FD_ALL_EVENTS )
      , out_event_flags( 0 )
      , out_event_index( -1 )
      , out_error      ( 0 )
    {}
    EventParam( EventParam const& ep )
    {
      memcpy( this, &ep, sizeof(*this) );
    }
  };

  // =============================================================================
  // レポート情報
  // 送信または受信中の処理中のリクエストに対する情報を取得のために用いる。
  // 実際に使う場合は、ReportInfoBufferを使うこと。
  struct ReportInfo {
    U32 socket_ID;
    U32 block_size;   // リクエストブロックサイズ。
    S64 size;         // 現在の（受信・送信済み）サイズ。
    U32 complete_num; // 送受信完了したセグメント数。
    U32 segment_num;  // 送受信中のセグメント数。
    U32 segment_size[ 1 ];  // セグメントサイズのテーブル。

    static int ComputeSize( U32 in_segment_num )
    {
      return sizeof(ReportInfo) + sizeof(U32)*(in_segment_num-1);
    }

  private:
    ReportInfo()
      : socket_ID( -1 )
      , size( 0 )
      , complete_num( false )
    {}
  };
  typedef Core::TypeMappedBuffer< ReportInfo > ReportInfoBuffer;
  typedef std::vector<ReportInfoBuffer>        ReportInfoBufferTable;

protected:
public:
  virtual SOCKET GetSOCKET() const = 0;

  //
  virtual void On_Accepted( _in<sdptr<Socket_i_>> in_sp_socket ) = 0;
  virtual void On_Closed  ( _in<sdptr<Socket_i_>> in_sp_socket ) = 0;

  virtual bool operator == ( SOCKET s ) const = 0;
  virtual bool operator != ( SOCKET s ) const = 0;

  virtual sockaddr const* GetSockAddr() const = 0;
  virtual sockaddr*       GetSockAddr() = 0;
  virtual void            SetSockAddr( sockaddr const* in_sockaddr, int in_size ) = 0;
  virtual U32             GetAddrLength() const = 0;

  //virtual int   CreateThread() = 0;
  virtual int   WaitEvents( EventParam& io_event_param ) const = 0;
  virtual void  ExitOn() = 0;
  virtual void  ExitOff() = 0;

  //virtual int   Send(
  //                SpByteBuffer in_sp_buffer,
  //                bool         in_flag_wait,
  //                bool         in_flag_copy ) = 0;
  //virtual int   Send(
  //                SpByteBuffer const in_buffer_table[],
  //                int                in_buf_table_num,
  //                bool               in_flag_wait,
  //                bool               in_flag_copy ) = 0;
  //virtual int   Recv( ByteBuffer& out_buffer ) = 0;
  virtual int   Close() = 0;

  virtual int   GetRecvReport( ReportInfoBufferTable& out_report_table ) const = 0;
  virtual int   GetSendReport( ReportInfoBufferTable& out_report_table ) const = 0;

  virtual void  SetSendPassword( cref<std::string> ) = 0;
  virtual void  SetRecvPassword( cref<std::string> ) = 0;
  virtual cref<std::string> GetSendPassword() const = 0;
  virtual cref<std::string> GetRecvPassword() const = 0;
};

typedef sdptr<Socket_i_> SpSocket;
typedef wkptr<Socket_i_> WpSocket;




} // namespace WIN
} // namespace CPL
