/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2008/X/X - 2014/02/11
/// 
/// @brief  メモリストリームセル。
/// 
/// ****************************************************************************

#include "../CPL_Core.h"

namespace CPL {
namespace Core {

// _____________________________________________________________________________
/// @brief バッファ設定付きコンストラクタ（派生用）
/// @param[in] in_sp_buffer : バッファクラスのスマートポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MemoryStreamCell::MemoryStreamCell( td_Buffer_sptr in_sp_buffer )
  : m_pos_base( 0 )
  , m_pos_end( static_cast<size_t>(in_sp_buffer->GetSize()) )
  , m_pos( 0 )
  , m_sp_buffer( in_sp_buffer )
{}

// _____________________________________________________________________________
/// @brief コンストラクタ
///
/// @param[in] in_buf_stm
///   : 参照するメモリストリームのアドレスを指定します。
/// @param[in] in_pos_base
///   : 管理する領域の先頭オフセット値（バイト単位）を指定します。\n
///   : 省略すると0となります。
/// @param[in] in_size
///   : 管理する領域のサイズ（バイト単位）を指定します。\n
///   : -1を指定すると最後尾を指します。\n
///   : 省略すると-1(最後尾)扱いとなります。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MemoryStreamCell::MemoryStreamCell
( pin<MemoryStreamCell> in_buf_stm
, _in<size_t          > in_pos_base
, _in<size_t          > in_size
) : m_sp_buffer( in_buf_stm->GetBuffer() )
{
  m_pos = m_pos_base = in_pos_base;
  auto buf_end = static_cast<size_t>(in_buf_stm->GetPosEnd());
  if( in_size < 0 )
  {
    m_pos_end = buf_end;
  }
  else
  {
    m_pos_end = m_pos_base + in_size;
    if( m_pos_end > buf_end )
    {
      m_pos_end = buf_end;
    }
  }
}

// _____________________________________________________________________________
/// @brief コピーコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MemoryStreamCell::MemoryStreamCell( cref<MemoryStreamCell> in_mem_stm )
  : m_pos_base ( static_cast<size_t>(in_mem_stm.GetPosTop()) )
  , m_pos_end  ( static_cast<size_t>(in_mem_stm.GetPosEnd()) )
  , m_pos      ( static_cast<size_t>(in_mem_stm.GetPos()   ) )
  , m_sp_buffer( in_mem_stm.GetBuffer()->CreateClone() )
{
}

// _____________________________________________________________________________
/// @brief ムーブコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MemoryStreamCell::MemoryStreamCell( MemoryStreamCell&& io_mem_stm )
  : m_pos_base ( static_cast<size_t>(io_mem_stm.GetPosTop()) )
  , m_pos_end  ( static_cast<size_t>(io_mem_stm.GetPosEnd()) )
  , m_pos      ( static_cast<size_t>(io_mem_stm.GetPos()   ) )
  , m_sp_buffer( io_mem_stm.GetBuffer() )
{
  zzz_buf_clear( io_mem_stm );
}

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


// _____________________________________________________________________________
/// @brief 部分メモリストリームの作成
///
/// @param[in] in_pos_offs
///   : 管理する領域の先頭オフセット値（バイト単位）を指定します。
/// @param[in] in_size
///   : 管理する領域のサイズ（バイト単位）を指定します。\n
///   : -1を指定すると最後尾を指します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<MemoryStreamCell>
  MemoryStreamCell::CreateStreamCell
    ( _in<U64> in_pos_offs
    , _in<U64> in_size
    ) const
{
  auto size = in_size;
  if( size > GetSize() - in_pos_offs )
  {
    size = GetSize() - in_pos_offs;
  }
  return
    CPL_MAKE_SHARED
      ( MemoryStreamCell
      , this
      , m_pos_base + static_cast<size_t>(in_pos_offs)
      , static_cast<size_t>(size)
      );
}


// _____________________________________________________________________________
/// @brief シーク処理
/// 
/// @param[in] in_offset
///   : 管理領域の指定位置からのオフセット（バイト単位）を指定します。
/// @param[in] in_origin
///   : 基準位置を指定します。\n
///   : SEEK_SET - 管理領域の先頭アドレスからのオフセットになります。\n
///   : SEEK_CUR - 現在位置からのオフセットになります。\n
///   : SEEK_END - 管理領域の終端アドレスからのオフセットになります。
/// 
/// @return 現在の仕様では必ず0が返ります。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int
  MemoryStreamCell::Seek
    ( _in<S64> in_offset
    , _in<int> in_origin
    ) const
{
  switch( in_origin )
  {
  case SEEK_SET: m_pos = m_pos_base + static_cast<size_t>(in_offset); break;
  case SEEK_CUR: m_pos += static_cast<size_t>(in_offset);             break;
  case SEEK_END: m_pos = m_pos_end - static_cast<size_t>(in_offset);  break;
  }
  if( m_pos < m_pos_base )
  {
    m_pos = m_pos_base;
  }
  else
  if( m_pos > m_pos_end )
  {
    m_pos = m_pos_end;
  }

  return 0;
}


// _____________________________________________________________________________
/// @brief メモリバッファストリームからの読み込み。
/// 
/// @param[out] out_ptr_buf : 読み出し先のアドレスを指定します。
/// @param[in]  in_size     : 読み込むサイズをバイト単位で指定します。
/// 
/// @return 実際に読み込まれたサイズを返します。\n失敗した場合、全ビットON(-1)を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64 MemoryStreamCell::Read( pout<void> out_ptr_buf, _in<U64> in_size ) const
{
  auto size = in_size;
  if( size <= 0 )
  {
    return (U64)-1;
  }

  auto buf_size = m_sp_buffer->GetSize();
  if( buf_size <= 0 )
  {
    return (U64)-1;
  }
  if( size + m_pos > buf_size )
  {
    size = buf_size - m_pos;
  }
  ::memcpy( out_ptr_buf, GetPosAddr(), static_cast<size_t>(size) );
  m_pos += static_cast<size_t>(size);

  return size;
}


// _____________________________________________________________________________
/// @brief メモリバッファストリームへ書き込み。
/// 
/// @param[in] in_ptr_buf : 書き込まれるデータを指すアドレスを指定します。
/// @param[in] in_size    : 書き込むサイズをバイト単位で指定します。
/// 
/// @return 実際に書き込まれたサイズを返します。\n失敗した場合、全ビットON(-1)を返します。
///
/// @note
/// MemoryStreamCellクラスでは、自身のサイズを超える領域には書き込みできない。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64 MemoryStreamCell::Write( pin<void> in_ptr_buf, _in<U64> in_size )
{
  if( in_size <= 0 )
  {
    return 0;
  }

  auto size = static_cast<size_t>(in_size);
  size_t buf_size = static_cast<size_t>(m_sp_buffer->GetSize());
  if( m_pos + size > buf_size )
  {
    // 書き込みサイズを調整
    size = buf_size - m_pos;
  }
  // データ書き込み
  ::memcpy  
    ( m_sp_buffer->GetDataAddrT<char*>( m_pos )
    , in_ptr_buf
    , size
    );
  // 位置補正
  m_pos += size;

  return size;
}



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




} // namespace Core
} // namespace CPL


