/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2014/02/06 - 2014/03/10
/// 
/// @brief  バッファマッピングクラス関連。
/// 
/// ****************************************************************************

#include "../CPL_Core.h"

namespace CPL {
namespace Core {


// _____________________________________________________________________________
/// @brief コンストラクタ。
/// 
/// @param[in] in_buf_size : バッファサイズ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DefaultBuffer::DefaultBuffer( _in<size_t> in_buf_size )
{
  if( in_buf_size )
  {
    m_buffer.resize( in_buf_size );
  }
}

// _____________________________________________________________________________
/// @brief コピーコンストラクタ。
/// 
/// @param[in] in_buf : コピー元のバッファクラス。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DefaultBuffer::DefaultBuffer( cref<DefaultBuffer> in_buf )
  : m_buffer( in_buf.m_buffer )
{
}

// _____________________________________________________________________________
/// @brief ムーブコンストラクタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DefaultBuffer::DefaultBuffer( rvref<DefaultBuffer> io_buf )
  : m_buffer( io_buf.m_buffer )
{
}

// _____________________________________________________________________________
/// @brief 汎用コピーコンストラクタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DefaultBuffer::DefaultBuffer( _in<Buffer_i> in_buf )
{
  m_buffer.reserve( static_cast<size_t>(in_buf.GetSize()) );
  memcpy
    ( &m_buffer[0]
    , in_buf.GetDataAddr()
    , static_cast<size_t>(in_buf.GetSize())
    );
}


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


// _____________________________________________________________________________
/// @brief 読み込み。
/// 
/// @param[in]  in_pos      : 読み込み開始位置をバイト単位で指定します。
/// @param[out] out_ptr_buf : 読み出し先のアドレスを指定します。
/// @param[in]  in_size     : 読み込むサイズをバイト単位で指定します。
/// @param[in]  in_type_access : DefaultBufferクラスでは使用しません。省略可。
/// 
/// @return 実際に読み込まれたサイズ。失敗した場合、全ビットON(-1)を返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64
  DefaultBuffer::Read
    ( _in <U64 > in_pos
    , pout<void> out_ptr_buf
    , _in <U64 > in_size
    , _in <U32 > in_type_access
    ) const
{
  Clamp<U64>( in_pos,  0, GetSize()-1 );
  Clamp<U64>( in_size, 0, GetSize() - in_pos );

  if( in_size > 0 )
  {
    memcpy
      ( out_ptr_buf
      , &m_buffer[ static_cast<size_t>(in_pos) ]
      , static_cast<size_t>(in_size)
      );
  }

  return in_size;
}


// _____________________________________________________________________________
/// @brief 書き込み。
/// 
/// @param[in] in_pos     : 書き込む位置をバイト単位で指定します。
/// @param[in] in_ptr_buf : 書き込まれるデータを指すアドレスを指定します。
/// @param[in] in_size    : 書き込むサイズをバイト単位で指定します。
/// @param[in] in_type_access : DefaultBufferクラスでは使用しません。省略可。
///
/// @return 実際に書き込まれたサイズ。失敗した場合、全ビットON(-1)を返す。
/// 
/// @note 
/// ※ in_sizeがバッファサイズを超える場合、バッファサイズを超える領域は無視されます。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64
  DefaultBuffer::Write
    ( _in<U64 > in_pos
    , pin<void> in_ptr_buf
    , _in<U64 > in_size
    , _in<U32 > in_type_access
    )
{
  Clamp<U64>( in_pos, 0, GetSize()-1 );
  Clamp<U64>( in_size, 0, GetSize() - in_pos );

  if( in_size > 0 )
  {
    memcpy
      ( &m_buffer[ static_cast<size_t>(in_pos) ]
      , in_ptr_buf
      , static_cast<size_t>(in_size)
      );
  }

  return in_size;
}

// _____________________________________________________________________________
/// @brief バッファリストの書き込み。
/// 
/// @param[in]  in_ptr_param_write
///   : 書き込みパラメータ配列の先頭ポインタを指定します。\n
///   : パラメータはstd::tuple<U64,void_cptr,U64>の構成とし、それぞれpos,address,sizeを表します。\n
/// @param[in]  in_num_param
///   : 書き込みパラメータ配列の要素数を指定します。
/// @param[out] out_ptr_result
///   : 各要素の実際に書き込まれたサイズを格納する配列へのポインタを指定します。\n
///   : 必要ない場合、nullptr指定できます。
/// @param[in] in_type_access : DefaultBufferクラスでは使用しません。省略可。
/// 
/// @return
/// std::pair<U64,U64>の形式を返す。\n
/// first  - 要求されたトータルサイズ。\n
/// second - 実際に書き込まれたトータルサイズ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
std::pair<U64,U64>
  DefaultBuffer::Write
    ( pin <std::tuple<U64,void_cptr,U64>> in_ptr_param_write
    , _in <size_t                       > in_num_param
    , pout<std::vector<U64>             > out_ptr_result
    , _in <U32                          > in_type_access
    )
{
  if( out_ptr_result )
  {
    out_ptr_result->resize( in_num_param );
  }

  U64 count_req  = 0; // リクエストされたサイズ
  U64 count_real = 0; // 
  for( size_t i = 0; i < in_num_param; ++i )
  {
    auto pos       = std::get<0>(in_ptr_param_write[i]);
    auto ptr_src   = std::get<1>(in_ptr_param_write[i]);
    auto size_req  = std::get<2>(in_ptr_param_write[i]);
    auto size_real = Write( pos, ptr_src, size_req );
    count_req  += size_req;
    count_real += size_real;
    if( out_ptr_result )
    {
      (*out_ptr_result)[i] = size_real;
    }
  }
  return std::pair<U64,U64>( count_req, count_real );
}

// _____________________________________________________________________________
/// @brief 挿入
/// 
/// @param[in] in_pos     : 挿入する位置をバイト単位で指定します。
/// @param[in] in_ptr_buf : 挿入するデータを指すアドレスを指定します。
/// @param[in] in_size    : 挿入するデータサイズをバイト単位で指定します。
/// @param[in] in_type_access : DefaultBufferクラスでは使用しません。省略可。
/// 
/// @return 実際に挿入されたサイズを返します。\n失敗した場合、全ビットON(-1)を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64
  DefaultBuffer::Insert
    ( _in<U64 > in_pos
    , pin<void> in_ptr_buf
    , _in<U64 > in_size
    , _in<U32 > in_type_access
    )
{
  auto src = reinterpret_cast<U8_cptr>(in_ptr_buf);
  if( in_pos > m_buffer.size() )
  {
    m_buffer.insert
      ( m_buffer.end()
      , src
      , src + static_cast<size_t>(in_size)
      );
  }
  else
  {
    m_buffer.insert
      ( m_buffer.begin() + static_cast<size_t>(in_pos)
      , src
      , src + static_cast<size_t>(in_size)
      );
  }
  return in_size;
}

// _____________________________________________________________________________
/// @brief 削除または切り取り。
/// 
/// @param[in]  in_pos_first
///   : 削除または切り取る開始位置をバイト単位で指定します。
/// @param[in]  in_pos_last
///   : 削除または切り取る末尾の位置をバイト単位で指定します。
/// @param[out] out_copy_buf
///   : 切り取る場合に保存先となるバッファのアドレス指定します。\n
///   : 必要ない場合はnullptrを指定することができます。
/// @param[in] in_type_access
///   : DefaultBufferクラスでは使用しません。省略可。
///
/// @return 削除されたサイズをバイト単位で返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64
  DefaultBuffer::Delete
    ( _in <U64 > in_pos_first
    , _in <U64 > in_pos_last
    , pout<void> out_copy_buf
    , _in <U32 > in_type_access
    )
{
  if( out_copy_buf )
  {
    memcpy
      ( out_copy_buf
      , GetDataAddrT<U8*>( static_cast<signed>(in_pos_first) )
      , static_cast<size_t>(in_pos_last - in_pos_first)
      );
  }
  m_buffer.erase
    ( m_buffer.begin() + static_cast<size_t>(in_pos_first)
    , m_buffer.begin() + static_cast<size_t>(in_pos_last)
    );

  return in_pos_last - in_pos_first;
}


// _____________________________________________________________________________
/// @copydoc Buffer_i::CreateClone
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
std::shared_ptr<Buffer_i> DefaultBuffer::CreateClone() const
{
  return CPL_MAKE_SHARED( DefaultBuffer, *this );
}





// _____________________________________________________________________________
/// @brief バッファ作成つきコンストラクタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
baseTypeMappedBuffer::baseTypeMappedBuffer( size_t in_size )
  : m_sp_buf( (in_size > 0) ? CPL_MAKE_SHARED( ByteBuffer, in_size ) : nullptr )
{}

// _____________________________________________________________________________
/// @brief クローン作成用基本関数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  baseTypeMappedBuffer::zzf_CreateClone(
    baseTypeMappedBuffer& out_mem_mapped ) const
{
  size_t size = m_sp_buf->size();
  if( size > 0 )
  {
    out_mem_mapped.m_sp_buf = CPL_MAKE_SHARED( ByteBuffer, size );
    CPL_MEMCPY( &(*out_mem_mapped.m_sp_buf)[0], &(*m_sp_buf)[0], size );
  }
  else
  {
    out_mem_mapped.m_sp_buf->clear();
  }
}

// _____________________________________________________________________________
/// @brief バッファサイズ取得。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t baseTypeMappedBuffer::GetBufferSize() const
{
  return m_sp_buf ? m_sp_buf->size() : 0;
}


// _____________________________________________________________________________
/// @brief バッファ作成。
///
/// @param[in] in_size : 作成するバッファサイズ
///
/// @return バッファサイズ(in_size)が返る。失敗した場合、-1（全ビットON）が返る。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t baseTypeMappedBuffer::CreateBuffer( _in<size_t> in_size )
{
  try {
    m_sp_buf = CPL_MAKE_SHARED( ByteBuffer, in_size );
    return in_size;
  }
  catch(...)
  {
    return -1;
  }
}


// _____________________________________________________________________________
/// @brief バッファリサイズ。
///
/// @param[in] in_size : 新しいサイズ。
///
/// @return バッファサイズ(in_size)が返る。失敗した場合、-1（全ビットON）が返る。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t baseTypeMappedBuffer::ResizeBuffer( _in<size_t> in_size )
{
  if( m_sp_buf )
  {
    try {
      m_sp_buf->resize( in_size );
      return in_size;
    }
    catch(...)
    {
      return -1;
    }
  }
  else
  {
    return CreateBuffer( in_size );
  }
}


// _____________________________________________________________________________
/// @brief バッファ挿入。
/// 
/// 挿入位置が要素数を超える場合、最後尾からデータが追加される。
///
/// @param[in] in_ins_pos  : 挿入する位置（先頭からのバイト単位オフセット）
/// @param[in] in_ptr_data : 挿入するデータのポインタ。
/// @param[in] in_ins_size : 挿入するデータのサイズ。
///
/// @return 
/// 成功した場合、拡張後のバッファサイズを返す。\n
/// 失敗した場合、-1（全ビットON）が返る。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t
  baseTypeMappedBuffer::InsertBuffer
    ( _in<size_t> in_ins_pos
    , pin<char  > in_ptr_data
    , _in<size_t> in_ins_size
    )
{
  auto ins_pos = in_ins_pos;
  // 後で使用するため、古いサイズを保存する。
  size_t old_size = m_sp_buf ? m_sp_buf->size() : 0;

  if( in_ins_size == 0 ) // 挿入サイズが０の場合。
  {
    return old_size;
  }
  else
  if( ins_pos > old_size ) // 挿入位置が旧バッファサイズを超えている場合。
  {
    ins_pos = old_size; // 挿入位置を最後尾に設定する。
  }
  
  //
  size_t new_size = ResizeBuffer( old_size + in_ins_size );
  if( new_size == -1 )
  {
    return -1;
  }

  // データ移動
  if( old_size > 0 )
  {
    U8_ptr ptr_end  = GetBufferAddress();
    U8_ptr ptr_dest = ptr_end + new_size - 1;
    U8_ptr ptr_src  = ptr_end + old_size - 1;
    while( ptr_end < ptr_src )
    {
      *ptr_dest = *ptr_src;

      --ptr_src;
      --ptr_dest;
    }
  }

  // 挿入データコピー。
  CPL_MEMCPY
    ( GetBufferAddress() + ins_pos
    , in_ptr_data
    , in_ins_size
    );

  return new_size;
}


// _____________________________________________________________________________
/// @brief バッファの指定範囲の削除。
///
/// バッファサイズが０(または作成されていない)場合、失敗とみなす。\n
/// 削除するサイズが０の場合、失敗とみなす。\n
/// 削除する位置が範囲外の場合、失敗とみなす。\n
/// 削除する範囲が超えている場合、調整される。
///
/// @param[in] in_del_pos
///   : 削除を開始する位置（先頭からのバイト単位オフセット）
/// @param[in] in_del_size
///   : 削除するサイズ（バイト単位）
///
/// @return 
/// 成功した場合、削除後のバッファサイズを返す。\n
/// 失敗した場合、-1（全ビットON）が返る。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t
  baseTypeMappedBuffer::EraseBuffer
    ( _in<size_t> in_del_pos
    , _in<size_t> in_del_size
    )
{
  if( !m_sp_buf )
  {
    return -1;
  }
  // 後で使用するため、古いサイズを保存する。
  size_t size_old = m_sp_buf ? m_sp_buf->size() : 0;
  auto   size_del = in_del_size;

  if( size_del == 0 ) // 削除サイズが０の場合。
  {
    return -1;
  }
  else
  if( in_del_pos > size_old )
  {
    return -1;
  }
  else
  if( in_del_pos + size_del > size_old )
  {
    size_del = size_old - in_del_pos;
  }

  // 削除
  auto itr_pos_st  = m_sp_buf->begin() + in_del_pos;
  auto itr_pos_end = itr_pos_st + (size_del - 1);
  m_sp_buf->erase( itr_pos_st, itr_pos_end );

  return m_sp_buf->size();
}

// _____________________________________________________________________________
/// @brief バイナリデータのコピー。
///
/// @param[in] in_data_addr : コピー元のアドレス。
/// @param[in] in_data_size : コピーサイズ。
///
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  baseTypeMappedBuffer::StoreBuffer
    ( pin<void  > in_data_addr
    , _in<size_t> in_data_size
    )
{
  size_t copy_size = Min( in_data_size, this->m_sp_buf->size() );
  CPL_MEMCPY( this->GetBufferAddress(), in_data_addr, copy_size );
}


} // namespace Core
} // namespace CPL
