/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2013/08/09 - 2013/08/13
/// 
/// @brief  バッファキューコンテナ
///
/// ****************************************************************************

#include "../CPL_Core.h"

namespace CPL {
namespace Core {


// _____________________________________________________________________________
/// @brief コンストラクタ
///
/// @param[in] in_ptr_mutex
///   : 同期用のミューテックスのポインタを指定します。\n
///   : 同期が必要ない場合、nullptrを指定できます。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BufferQueue::BufferQueue( ptr<Core::MutexBase> in_ptr_mutex )
  : m_ptr_mutex( in_ptr_mutex )
{
}


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


// _____________________________________________________________________________
/// @brief データをキューの最後尾に追加
///
/// @param[in] in_data_addr
///   : 追加データへのアドレスを指定します。\n
///   : nullptrを指定すると、空のバッファが追加されます。
/// @param[in] in_data_size
///   : 追加データサイズを指定します。\n
///   : 0を指定すると、空のバッファが追加されます。
///
/// @return キュー追加後の要素数を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t BufferQueue::Push( pin<void> in_data_addr, _in<size_t> in_data_size )
{
  auto locking = LockMutex( m_ptr_mutex );

  // 入力バッファが有効な場合。
  if( in_data_addr != nullptr  &&  in_data_size > 0 )
  {
    // 現在のバッファがサイズが0の場合、そのバッファを拡張してコピー。
    if( !m_buf_queue.empty()  &&  m_buf_queue.back()->size() == 0 )
    {
      // ポインタ取得 ＆ 拡張。
      SpByteBuffer sp_new_buf = m_buf_queue.back();
      sp_new_buf->resize( in_data_size );
      // コピー
      CPL_MEMCPY( &(*sp_new_buf)[0], in_data_addr, in_data_size );
    }
    // 
    else
    {
      // 新規作成。
      SpByteBuffer sp_new_buf = std::make_shared<ByteBuffer>( in_data_size );
      // コピー
      CPL_MEMCPY( &(*sp_new_buf)[0], in_data_addr, in_data_size );
      // 追加
      m_buf_queue.push( sp_new_buf );
    }
  }
  else // 入力バッファが無効の場合。
  {
    // キューの最後の要素のバッファサイズが０の場合、何もせずに抜ける。
    // つまり、空のバッファの後ろに空のバッファを追加しない。
    if( !m_buf_queue.empty()  &&  m_buf_queue.back()->size() == 0 )
    {
      return GetQueueNum();
    }
    else
    {
      // 空のバッファを追加。
      m_buf_queue.push( std::make_shared<ByteBuffer>() );
    }

  }

  return GetQueueNum();
}


// _____________________________________________________________________________
/// @brief データをキューの最後尾に追加
///
/// @param[in] in_buffer
///   : 追加データが格納されたバッファのアドレスを指定します。\n
///   : サイズが0のバッファを渡すと、空のバッファをキューに追加します。
///
/// @return キュー追加後の要素数を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t BufferQueue::Push( _in<ByteBuffer> in_buffer )
{
  auto locking = LockMutex( m_ptr_mutex );

  size_t size = in_buffer.size();

  // 入力バッファが有効な場合。
  if( size > 0 )
  {
    // 現在のバッファがサイズが0の場合、そのバッファを拡張してコピー。
    if( !m_buf_queue.empty()  &&  m_buf_queue.back()->size() == 0 )
    {
      SpByteBuffer sp_new_buf = m_buf_queue.back();
      sp_new_buf->resize( size );
      CPL_MEMCPY( &(*sp_new_buf)[0], &in_buffer[0], size );
    }
    else
    {
      // 新規作成。
      SpByteBuffer sp_new_buf = std::make_shared<ByteBuffer>( size );
      // コピー。
      CPL_MEMCPY( &(*sp_new_buf)[0], &in_buffer[0], size );
      // 追加。
      m_buf_queue.push( sp_new_buf );
    }
  }
  else
  {
    // キューの最後の要素のバッファサイズが０の場合、何もせずに抜ける。
    // つまり、空のバッファの後ろに空のバッファを追加しない。
    if( !m_buf_queue.empty()  &&  m_buf_queue.back()->size() == 0 )
    {
      return GetQueueNum();
    }
    else
    {
      // 空のバッファを追加。
      m_buf_queue.push( std::make_shared<ByteBuffer>() );
    }
  }

  return GetQueueNum();
}



// _____________________________________________________________________________
/// @brief データをキューの最後尾に追加
///
/// @param[in] in_sp_buffer
///   : 追加データを指すバッファオブジェクトのスマートポインタを指定します。\n
///   : nullptrを指定すると、空のバッファを追加します。
/// @param[in] in_flag_copy
///   : バッファを複製するか、所有権を保持するかを指定します。\n
///   : true  - 新しくバッファを作成し、渡されたバッファの内容をコピーします。\n
///   : false - 渡されたバッファの所有権を保持します。
///
/// @return キュー追加後の要素数を返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t
  BufferQueue::Push
    ( _in<SpByteBuffer> in_sp_buffer
    , _in<bool        > in_flag_copy
    )
{
  auto locking = LockMutex( m_ptr_mutex );

  // 入力バッファが有効な場合。
  if( in_sp_buffer != nullptr )
  {
    auto new_size = in_sp_buffer->size();

    if( new_size > 0 ) // 入力バッファが確保されている場合。
    {
      // キューのアクティブなバッファが確保されていない場合。
      if( !m_buf_queue.empty()  &&  m_buf_queue.back()->size() == 0 )
      {
        // コピーする場合、バッファを拡張してコピー。
        if( in_flag_copy )
        {
          SpByteBuffer sp_new_buf = m_buf_queue.back();
          sp_new_buf->resize( new_size );
          CPL_MEMCPY( &(*sp_new_buf)[0], &(*in_sp_buffer)[0], new_size );
        }
        // コピーしない場合。ポインタをそのまま設定。
        else
        {
          m_buf_queue.back() = in_sp_buffer;
        }
      }
      else // 最後尾の要素のバッファが確保されている場合。
      {
        // コピーする場合。
        if( in_flag_copy )
        {
          // 新規作成。
          SpByteBuffer sp_new_buf = std::make_shared<ByteBuffer>( new_size );
          // コピー。
          CPL_MEMCPY( &(*sp_new_buf)[0], &(*in_sp_buffer)[0], new_size );
          // 追加。
          m_buf_queue.push( sp_new_buf );
        }
        else
        {
          m_buf_queue.push( in_sp_buffer );
        }
      }
    }
    else // 入力バッファが確保されていない場合。
    {
      if( in_flag_copy )
      {
        // 空のバッファを追加。
        m_buf_queue.push( std::make_shared<ByteBuffer>() );
      }
      else
      {
        m_buf_queue.push( in_sp_buffer );
      }
    }
  }
  else // 入力バッファが無効の場合。
  {
    // キューの最後の要素のバッファサイズが0の場合、何もせずに抜ける。
    // つまり、空のバッファの後ろに空のバッファを追加しない。
    if( !m_buf_queue.empty()  &&  m_buf_queue.back()->size() == 0 )
    {
      return GetQueueNum();
    }
    else
    {
      // 空のバッファを追加。
      m_buf_queue.push( std::make_shared<ByteBuffer>() );
    }
  }

  return GetQueueNum();
}



// _____________________________________________________________________________
/// @brief キューの最後尾の要素のバッファにデータを追記する。
///
/// @param[in] in_ptr_data
///   : 追記データへのアドレス。nullptrは失敗とする。
/// @param[in] in_data_size
///   : 追記データサイズ。
///
/// @retval true  : 成功
/// @retval false : 失敗
///
/// @note
/// キューに要素が無い場合、そのまま追加する。\n
/// AdditionはPushと違い、空のバッファを指定しても空のバッファは追加されない。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool BufferQueue::Addition( pin<void> in_ptr_data, _in<size_t> in_data_size )
{
  if( in_ptr_data == nullptr  ||  in_data_size == 0 )
  {
    return false;
  }

  auto locking = LockMutex( m_ptr_mutex );

  if( m_buf_queue.empty() )
  {
    Push( in_ptr_data, in_data_size );
    return true;
  }
  else
  {
    auto& sp_buf = m_buf_queue.back();
    sp_buf->insert
      ( sp_buf->begin()
      , (U8_cptr)in_ptr_data
      , (U8_cptr)in_ptr_data + in_data_size
      );
    return true;
  }
}


// _____________________________________________________________________________
/// @brief キューの最後尾の要素のバッファにデータを追記する。
///
/// @param[in] in_data
///   : 追記データが格納されたバッファ。
///
/// @retval true  : 成功。
/// @retval false : 失敗。
///
/// @note
/// キューに要素が無い場合、そのまま追加する。\n
/// AdditionはPushと違い、空のバッファを指定しても空のバッファは追加されない。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool BufferQueue::Addition( _in<ByteBuffer> in_data )
{
  if( in_data.size() == 0 )
  {
    return false;
  }

  return Addition( &in_data[0], in_data.size() );
}


// _____________________________________________________________________________
/// @brief キューの最後尾の要素のバッファにデータを追記する。
///
/// @param[in] in_sp_data
///   : 追記データが格納されたバッファへのポインタ。
///
/// @retval true  : 成功。
/// @retval false : 失敗。
///
/// @note
/// キューに要素が無い場合、そのまま追加する。\n
/// AdditionはPushと違い、空のバッファを指定しても空のバッファは追加されない。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool BufferQueue::Addition( _in<SpByteBuffer> in_sp_data )
{
  if( in_sp_data == nullptr )
  {
    return false;
  }

  return Addition( *in_sp_data );
}




// _____________________________________________________________________________
/// @brief 先頭の要素を削除、つまりポップする。
///
/// @return ポップ前の先頭の要素を返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SpByteBuffer BufferQueue::Pop() const
{
  auto locking = LockMutex( m_ptr_mutex );

  if( IsEmpty() )
  {
    return nullptr;
  }

  auto sp_bbuf = m_buf_queue.front();

  m_buf_queue.pop();

  return sp_bbuf;
}



// _____________________________________________________________________________
/// @brief 先頭データの抜き出し。
///
/// @param[out] out_buffer
///   : コピー先バッファ。
///
/// @retval true  : 成功。
/// @retval false : 失敗。
///
/// @note 
/// 先頭のバッファをコピーし、バッファがクリアされる。
/// ポップは行わない。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool BufferQueue::Subtraction( rout<ByteBuffer> out_buffer )
{
  auto locking = LockMutex( m_ptr_mutex );

  if( IsEmpty() )
  {
    return false;
  }

  //
  auto& sp_buf = m_buf_queue.front();

  // コピー
  out_buffer.insert
    ( out_buffer.end()
    , sp_buf->begin()
    , sp_buf->end()
    );

  // クリア
  sp_buf->clear();

  //
  return true;
}


} // namespace Core
} // namespace CPL