/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2013/X/X - 2015/08/03
/// 
/// @brief  コンテナ　チャンクマネージャクラス
/// 
/// ****************************************************************************

#include "../CPL_Core.h"

namespace CPL {
namespace Core {


// _____________________________________________________________________________
/// @brief コンストラクタ。
///
/// @param[in] in_version   : バージョン（8bit）
/// @param[in] in_type      : チャンクタイプを表す識別文字列。
/// @param[in] in_data_size : データ部分のサイズ（ヘッダは含めない）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkManager::ChunkManager
( _in<U8  > in_version
, pin<char> in_type
, _in<U64 > in_data_size
)
{
  // 初期化。
  Initialize( in_version, in_type, in_data_size );
}

// _____________________________________________________________________________
/// @brief デストラクタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkManager::~ChunkManager()
{
  // 
  //DEL_CLEAR( m_up_chunklink );
}


// _____________________________________________________________________________
/// @brief 代入（チャンクマネージャ渡し仕様）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ChunkManager::operator = ( cref<ChunkManager> in_chunk_mng )
{
  m_buffer = in_chunk_mng.m_buffer;
  zzf_resetChunkLink();
}


// _____________________________________________________________________________
/// @brief 代入（チャンクデータ渡し）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ChunkManager::operator = ( cref<ChunkBase> in_chunk )
{
  size_t size = static_cast<size_t>(in_chunk.GetSize());

  m_buffer.resize( size );
  CPL_MEMCPY( &m_buffer[0], &in_chunk, size );
  //
  zzf_resetChunkLink();
}


// _____________________________________________________________________________
/// @brief チャンクマネージャ初期化。
///
/// @param[in] in_version
///   : バージョン（Max 8bit）
/// @param[in] in_type
///   : チャンクタイプを表す識別文字列。
/// @param[in] in_data_size
///   : データ部分のサイズ（ヘッダは含めない）\n
///   : 型は64bitになっているが、指定できる最大値はチャンクバージョンに依存する。\n
///   : また、32bitコンパイルでは32bit以上扱えるチャンクでも、マネージャでは32bitとなる。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  ChunkManager::Initialize
    ( _in<U8  > in_version
    , pin<char> in_type
    , _in<U64 > in_data_size
    )
{
  size_t buf_size = 0;
  switch( in_version )
  {
  case 0:
    buf_size =
      static_cast<size_t>(ChunkInfo0::CalculateBufferSize( in_type, in_data_size ));
    break;
  }

  if( buf_size > 0 )
  {
    // バッファ作成＆クリア
    m_buffer.resize( buf_size, 0 );
    //
    auto p_check = &m_buffer[0];

    switch( in_version )
    {
    case 0:
      { ChunkInfo0* ptr_chunk = reinterpret_cast<ChunkInfo0*>(p_check);
        ptr_chunk->Initialize( in_type, false, false, in_data_size );
      } break;

    default: // 現在version0しかないので、同じ処理だが...
      { ChunkInfo0* ptr_chunk = reinterpret_cast<ChunkInfo0*>(p_check);
        ptr_chunk->Initialize( in_type, false, false, in_data_size );
      } break;
    }

    // チャンクリンクの再構築。
    zzf_resetChunkLink();
  }
}



// _____________________________________________________________________________
/// @brief チャンクリンカーの検索。
///
/// 自身および全ての子供の中から、指定されたチャンクを保持するチャンクリンクを検索。
/// 比較はデータ要素ではなく、ポインタによる比較。
///
/// @param[in] in_ptr_chunk : 比較するチャンクポインタ。
///
/// @return 該当したチャンクリンクポインタ。該当しなければnullptrを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkLinker*
  ChunkManager::FindChunkLinker( pin<ChunkBase> in_ptr_chunk )
{
  if( in_ptr_chunk == nullptr )
  {
    return nullptr;
  }

  auto ptr_chunklink = m_up_chunklink.get();
  while( ptr_chunklink )
  {
    auto ret = ptr_chunklink->FindChunkLinker( in_ptr_chunk );
    if( ret )
    {
      return ret;
    }
    ptr_chunklink = ptr_chunklink->GetChunkNext();
  }
  return nullptr;
}

// _____________________________________________________________________________
/// @brief チャンクリンカーの検索。（const仕様）
///
/// 自身および全ての子供の中から、指定されたチャンクを保持するチャンクリンクを検索。\n
/// 比較はデータ要素ではなく、ポインタによる比較。
///
/// @param[in] in_ptr_chunk : 比較するチャンクポインタ。
///
/// @return 該当したチャンクリンクポインタ。該当しなければnullptrを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkLinker const*
  ChunkManager::FindChunkLinker( pin<ChunkBase> in_ptr_chunk ) const
{
  auto ptr_chunklink = m_up_chunklink.get();
  while( ptr_chunklink )
  {
    auto ret = ptr_chunklink->FindChunkLinker( in_ptr_chunk );
    if( ret )
    {
      return ret;
    }
    ptr_chunklink = ptr_chunklink->GetChunkNext();
  }
  return nullptr;
}

// _____________________________________________________________________________
/// @brief チャンクリンカーの検索。
///
/// 自身および全ての子供の中から、指定されたチャンクを保持するチャンクリンクを検索。\n
/// 比較はポインタではなく、データの要素同士で比較する。
///
/// @param[in] in_chunk
///   : 比較するチャンクデータが格納されたチャンクへの参照。
/// @param[in] in_matching_mask
///   : 比較するパラメータを選択するためのマスク値。
///
/// @return 該当したチャンクリンクポインタ。該当しなければnullptrを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkLinker*
  ChunkManager::FindChunkLinker
    ( _in<ChunkBase> in_chunk
    , _in<U32      > in_matching_mask
    )
{
  auto ptr_chunklink = m_up_chunklink.get();
  while( ptr_chunklink )
  {
    auto ret = ptr_chunklink->FindChunkLinker( in_chunk, in_matching_mask );
    if( ret )
    {
      return ret;
    }
    ptr_chunklink = ptr_chunklink->GetChunkNext();
  }
  return nullptr;
}

// _____________________________________________________________________________
/// @brief チャンクリンカーの検索。※ const仕様
///
/// 自身および全ての子供の中から、指定されたチャンクを保持するチャンクリンクを検索。\n
/// 比較はポインタではなく、データの要素同士で比較する。
///
/// @param[in] in_chunk
///   : 比較するチャンクデータが格納されたチャンクへの参照。
/// @param[in] in_matching_mask
///   : 比較するパラメータを選択するためのマスク値。
///
/// @return 該当したチャンクリンクポインタ。該当しなければnullptrを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkLinker const*
  ChunkManager::FindChunkLinker
    ( _in<ChunkBase> in_chunk
    , _in<U32      > in_matching_mask
    ) const
{
  auto ptr_chunklink = m_up_chunklink.get();
  while( ptr_chunklink )
  {
    auto ret = ptr_chunklink->FindChunkLinker( in_chunk, in_matching_mask );
    if( ret )
    {
      return ret;
    }
    ptr_chunklink = ptr_chunklink->GetChunkNext();
  }
  return nullptr;
}

// _____________________________________________________________________________
/// @brief 自身および全ての子供の中から指定されたタイプのチャンクを全て検索する。
///
/// @param[in] in_chunk
///   : マッチング対象のチャンクデータ。※ 実チャンクである必要は無い。
/// @param[in] in_mask
///   : マッチングの際に比較項目を選択するためのマスクフラグ。
/// @param[out] out_list
///   : 検索されたチャンクのポインタを追加する配列クラスへの参照。
///
/// @return 条件と一致したチャンク数を返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int
  ChunkManager::FindChunkLinker
    ( cref<ChunkBase      > in_chunk
    , _in <U32            > in_mask
    , rout<ChunkLinkPtrArr> out_list
    )
{
  out_list.clear();

  auto ptr_chunklink = m_up_chunklink.get();
  while( ptr_chunklink )
  {
    ptr_chunklink->FindChunkLinker( in_chunk, in_mask, out_list );
    ptr_chunklink = ptr_chunklink->GetChunkNext();
  }
  return static_cast<int>(out_list.size());
}

// _____________________________________________________________________________
/// @brief
/// 自身および全ての子供の中から指定されたタイプのチャンクを全て検索する。※ const仕様
///
/// @param[in] in_chunk
///   : マッチング対象のチャンクデータ。※ 実チャンクである必要は無い。
/// @param[in] in_mask
///   : マッチングの際に比較項目を選択するためのマスクフラグ。
/// @param[out] out_list
///   : 検索されたチャンクのポインタを追加する配列クラスへの参照。
///
/// @return 条件と一致したチャンク数を返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int
  ChunkManager::FindChunkLinker
    ( cref<ChunkBase       > in_chunk
    , _in <U32             > in_mask
    , rout<ChunkLinkCPtrArr> out_list
    ) const
{
  out_list.clear();

  auto ptr_chunklink = m_up_chunklink.get();
  while( ptr_chunklink )
  {
    ptr_chunklink->FindChunkLinker( in_chunk, in_mask, out_list );
    ptr_chunklink = ptr_chunklink->GetChunkNext();
  }
  return static_cast<int>(out_list.size());
}


// _____________________________________________________________________________
/// @brief
/// 指定されたチャンクを保持しているかチェック。\n
/// ※ 比較はデータマッチングではなく、ポインタ比較。
///
/// @param[in] in_ptr_chunk : 比較するチャンクのポインタ。
///
/// @retval true  : 含まれている。
/// @retval false : 含まれていない。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ChunkManager::IsChunkInclude( pin<ChunkBase> in_ptr_chunk ) const
{
  auto ptr_chunklink = m_up_chunklink.get();
  while( ptr_chunklink )
  {
    if( ptr_chunklink->IsChunkInclude( in_ptr_chunk ) )
    {
      return true;
    }

    ptr_chunklink = ptr_chunklink->GetChunkNext();
  }
  return false;
}

// _____________________________________________________________________________
/// @brief
/// 指定されたチャンクリンカーを保持しているかチェック。\n
/// ※ 比較はデータマッチングではなく、ポインタ比較。
///
/// @param[in] in_ptr_chunklink : 比較するチャンクのポインタ。
///
/// @retval true  : 含まれている。
/// @retval false : 含まれていない。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ChunkManager::IsChunkInclude( ChunkLinkCPtr in_ptr_chunklink ) const
{
  auto ptr_chunklink = m_up_chunklink.get();
  while( ptr_chunklink )
  {
    auto p = in_ptr_chunklink->GetHeader();
    if( ptr_chunklink->IsChunkInclude( p ) )
    {
      return true;
    }

    ptr_chunklink = ptr_chunklink->GetChunkNext();
  }
  return false;
}



// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// データ渡し仕様のチャンク追加汎用関数。
//  引数 :
//    in_ptr_chunklink_crit : 追加位置基準チャンク。
//    in_version   : 追加するチャンクフォーマットのバージョン。
//    in_type      : チャンク識別名。
//    in_ptr_data  : 追加するチャンクのデータ部へのポインタ。
//    in_data_size : 追加するチャンクのデータ部のサイズ。
//  戻り値 :
//    追加されたチャンクを差すポインタ。
//    未対応バージョンの場合、nullptrを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkBase*
  ChunkManager::zzf_addChunkXXX(
    ChunkLinker* in_ptr_chunklink_crit,
    U8           in_version,
    char_cptr    in_type,
    void_cptr    in_ptr_data,
    U64          in_data_size,
    fpAddChunkX  func_AddChunkX )
{
  // 新しいチャンクのサイズを求める。
  size_t new_chunk_size = 0;
  switch( in_version )
  {
  case 0:
    new_chunk_size =
      static_cast<size_t>(ChunkInfo0::CalculateBufferSize( in_type, in_data_size ));
    break;

  default:
    return nullptr;
  }

  // 追加用のチャンクデータを作成するための一時バッファ。
  Array::D_Char tmp_buf( new_chunk_size );

  // 追加チャンクデータの設定。
  switch( in_version )
  {
  case 0:
  default: // 現在(2013/08/29)ver0しかないので...
    {
      ChunkInfo0* ptr_chunk_header = reinterpret_cast<ChunkInfo0*>(&tmp_buf[0]);
      ptr_chunk_header->Initialize( in_type, true, false, in_data_size );
      CPL_MEMCPY(
        ptr_chunk_header->GetDataAddr(),
        in_ptr_data,
        static_cast<size_t>(in_data_size) );
      return (this->*func_AddChunkX)( in_ptr_chunklink_crit, ptr_chunk_header );
    }
  }

}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// チャンクサイズ調整。
//  param:
//    in_ptr_chunklink_target : 変更対象のチャンクリンカー。
//    in_size_offs            : 現在のサイズから拡張サイズ（縮小する場合、マイナスとなる）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void zzf_adjustChunkSize_( ChunkLinker* io_ptr_chunklink_target, S64 in_size_offs )
{
  auto ptr_chunklink = io_ptr_chunklink_target;

  while( ptr_chunklink )
  {
    auto ptr_chunk = ptr_chunklink->GetHeader();

    ptr_chunk->SetSize( ptr_chunk->GetSize() + in_size_offs );

    ptr_chunklink = ptr_chunklink->GetChunkParent();
  }
}




// _____________________________________________________________________________
/// @brief
/// 指定されたチャンクの前のチャンクとして追加　※ チャンクリンカー渡し仕様。
///
/// @param[in] in_ptr_chunklink_crit : 追加位置基準チャンクに関連付けられているリンカー。
/// @param[in] in_ptr_chunk_add      : 追加データが設定されたチャンクへのポインタ。
///
/// @return 追加されたチャンクを差すポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkBase*
  ChunkManager::AddChunkPrev
    ( ChunkLinkPtr   in_ptr_chunklink_crit
    , pin<ChunkBase> in_ptr_chunk_add
    )
{
  // 指定チャンクを保持しているかチェック。
  if( !IsChunkInclude( in_ptr_chunklink_crit ) )
  {
    return nullptr; // 保持していない場合。nullを返す。
  }

  // 追加位置を計算。
  size_t insert_byte_offs =
    (char_cptr)in_ptr_chunklink_crit->GetHeader() - &m_buffer[0];
  // 追加サイズ取得。
  SDEF add_chunk_size = static_cast<SDEF>(in_ptr_chunk_add->GetSize());

  // データを追加する前に、変更の必要のあるチャンクの情報を書き換える。
  // ※ データを追加するとアドレスが変わる可能性があるため。
  zzf_adjustChunkSize_(
    in_ptr_chunklink_crit->GetChunkParent(),
    add_chunk_size );

  // データを追加。
  m_buffer.insert(
    m_buffer.begin() + insert_byte_offs,
    reinterpret_cast<char_cptr>(in_ptr_chunk_add),
    reinterpret_cast<char_cptr>(in_ptr_chunk_add) + add_chunk_size );

  // 追加されたチャンクのネクストフラグを有効化。
  ChunkInfo0*
    ptr_add_chunk =
      reinterpret_cast<ChunkInfo0*>(&m_buffer[insert_byte_offs]);
  ptr_add_chunk->SetNextFlag( true );

  // チャンクリンクの再構築。
  zzf_resetChunkLink();

  return ptr_add_chunk;
}


// _____________________________________________________________________________
/// @brief 指定されたチャンクの前のチャンクとして追加。
///
/// @param[in] in_ptr_chunk_crit : 追加位置基準チャンク。
/// @param[in] in_ptr_chunk_add  : データが設定された追加チャンクへのポインタ。
///
/// @return 追加されたチャンクを差すポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkBase*
  ChunkManager::AddChunkPrev
    ( pin<ChunkBase> in_ptr_chunk_crit
    , pin<ChunkBase> in_ptr_chunk_add
    )
{
  // 基準チャンクが自身が保持するチャンクかチェック。
  if( IsChunkInclude( in_ptr_chunk_crit ) )
  {
    return AddChunkPrev( in_ptr_chunk_crit, in_ptr_chunk_add );
  }
  return nullptr;
}

// _____________________________________________________________________________
/// @brief 指定されたチャンクの前のチャンクとして追加　※ データ渡し仕様。
///
/// @param[in] in_ptr_chunklink_crit : 追加位置基準チャンクに関連付けられているリンカー。
/// @param[in] in_version            : 追加するチャンクフォーマットのバージョン。
/// @param[in] in_type               : チャンク識別名。
/// @param[in] in_ptr_data           : 追加するチャンクのデータ部へのポインタ。
/// @param[in] in_data_size          : 追加するチャンクのデータ部のサイズ。
///
/// @return 追加されたチャンクを差すポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkBase*
  ChunkManager::AddChunkPrev
    ( ChunkLinkPtr in_ptr_chunklink_crit
    , U8           in_version
    , char_cptr    in_type
    , void_cptr    in_ptr_data
    , U64          in_data_size
    )
{
  return
    zzf_addChunkXXX
      ( in_ptr_chunklink_crit
      , in_version
      , in_type
      , in_ptr_data
      , in_data_size
      , &ChunkManager::AddChunkPrev
      );
}


// _____________________________________________________________________________
/// @brief 指定されたチャンクの前のチャンクとして追加　※ データ渡し仕様。
///
/// @param[in] in_ptr_chunk_crit : 追加位置基準チャンク。
/// @param[in] in_version        : 追加するチャンクフォーマットのバージョン。
/// @param[in] in_type           : チャンク識別名。
/// @param[in] in_ptr_data       : 追加するチャンクのデータ部へのポインタ。
/// @param[in] in_data_size      : 追加するチャンクのデータ部のサイズ。
///
/// @return 追加されたチャンクを差すポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkBase*
  ChunkManager::AddChunkPrev
    ( pin<ChunkBase> in_ptr_chunk_crit
    , U8             in_version
    , char_cptr      in_type
    , void_cptr      in_ptr_data
    , U64            in_data_size
    )
{
  auto ptr_chunklink = FindChunkLinker( in_ptr_chunk_crit );
  if( ptr_chunklink )
  {
    return
      zzf_addChunkXXX
        ( ptr_chunklink
        , in_version
        , in_type
        , in_ptr_data
        , in_data_size
        , &ChunkManager::AddChunkPrev
        );
  }
  return nullptr;
}



// _____________________________________________________________________________
/// @brief
/// 指定されたチャンクの次のチャンクとして追加　※ チャンクリンカー渡し仕様。
///
/// @param[in] in_ptr_chunklink_crit : 追加位置基準チャンクに関連付けられているリンカー。
/// @param[in] in_ptr_chunk_add      : 追加データが設定されたチャンクへのポインタ。
///
/// @return 追加されたチャンクを差すポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkBase*
  ChunkManager::AddChunkNext
    ( ChunkLinkPtr   in_ptr_chunklink_crit
    , pin<ChunkBase> in_ptr_chunk_add )
{
  // 指定チャンクを保持しているかチェック。
  if( !IsChunkInclude( in_ptr_chunklink_crit ) )
  {
    return nullptr; // 保持していない場合。nullを返す。
  }

  // 追加対象チャンクのアドレスをバイト型で取得。
  auto ptr_chunk_target  = in_ptr_chunklink_crit->GetHeader();
  auto target_chunk_addr = (char_cptr)ptr_chunk_target;
  size_t target_byte_offs = target_chunk_addr - &m_buffer[0];
  // 追加位置を計算。
  size_t target_chunk_size = static_cast<size_t>(in_ptr_chunklink_crit->GetSize());
  size_t insert_byte_offs  = target_byte_offs + target_chunk_size;
  // 追加サイズ取得。
  SDEF insert_chunk_size = static_cast<SDEF>(in_ptr_chunk_add->GetSize());
  
  //*** バッファ拡張前処理                                     ***//
  //*** ※ データを追加するとアドレスが変わる可能性があるため。***//

  // データを追加する前に、変更の必要のあるチャンクのサイズを書き換える。
  zzf_adjustChunkSize_(
    in_ptr_chunklink_crit->GetChunkParent(),
    insert_chunk_size );

  bool is_next_chunk = false; // 次チャンクフラグ(ver.0～)

  switch( ptr_chunk_target->GetVersion() )
  {
  case 0:
    // 追加位置対象のチャンクのネクストフラグを保持。
    // （追加チャンクに適用する必要があるが、データの追加でリンカーが無効になる可能性があるため。）
    is_next_chunk = in_ptr_chunklink_crit->IsChunkNext();
    break;
  }

  // データを追加。
  m_buffer.insert(
    m_buffer.begin() + insert_byte_offs,
    reinterpret_cast<char_cptr>(in_ptr_chunk_add),
    reinterpret_cast<char_cptr>(in_ptr_chunk_add) + insert_chunk_size );

  //****** バッファ拡張後処理 ******//

  ChunkBase*
  ptr_chunk_add    = reinterpret_cast<ChunkBase*>(&m_buffer[insert_byte_offs]);
  ptr_chunk_target = reinterpret_cast<ChunkBase*>(&m_buffer[target_byte_offs]);
  switch( ptr_chunk_target->GetVersion() )
  {
  case 0:
    // 追加されたチャンクのネクストフラグを設定。
    static_cast<ChunkInfo0*>(ptr_chunk_add)->SetNextFlag( is_next_chunk );
    // 追加されたチャンクの前方のチャンクのネクストフラグを設定。
    static_cast<ChunkInfo0*>(ptr_chunk_target)->SetNextFlag( true );
    break;
  }

  
  // チャンクリンクの再構築。
  zzf_resetChunkLink();
  
  return ptr_chunk_add;
}

// _____________________________________________________________________________
/// @brief
/// 指定されたチャンクの次のチャンクとして追加　※ チャンクポインタ渡し仕様。
///
/// @param[in] in_ptr_chunk_crit : 追加位置基準チャンク。
/// @param[in] in_ptr_chunk_add  : データが設定された追加チャンクへのポインタ。
///
/// @return 追加されたチャンクを差すポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkBase*
  ChunkManager::AddChunkNext
    ( pin<ChunkBase> in_ptr_chunk_crit
    , pin<ChunkBase> in_ptr_chunk_add
    )
{
  auto ptr_chunklink = FindChunkLinker( in_ptr_chunk_crit );
  if( ptr_chunklink )
  {
    return AddChunkNext( ptr_chunklink, in_ptr_chunk_add );
  }
  
  return nullptr;
}


// _____________________________________________________________________________
/// @brief
/// 指定されたチャンクの次のチャンクとして追加　※ チャンクリンク、データ渡し仕様。
///
/// @param[in] in_ptr_chunklink_crit : 追加位置基準チャンクに関連付けられているリンカー。
/// @param[in] in_version            : 追加するチャンクフォーマットのバージョン。
/// @param[in] in_type               : チャンク識別名。
/// @param[in] in_ptr_data           : 追加するチャンクのデータ部へのポインタ。
/// @param[in] in_data_size          : 追加するチャンクのデータ部のサイズ。
///
/// @return 追加されたチャンクを差すポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkBase*
  ChunkManager::AddChunkNext
    ( ChunkLinkPtr in_ptr_chunklink_crit
    , U8           in_version
    , char_cptr    in_type
    , void_cptr    in_ptr_data
    , U64          in_data_size
    )
{
  return
    zzf_addChunkXXX
      ( in_ptr_chunklink_crit
      , in_version
      , in_type
      , in_ptr_data
      , in_data_size
      , &ChunkManager::AddChunkNext
      );
}


// _____________________________________________________________________________
/// @brief 指定されたチャンクの次のチャンクとして追加　※ データ渡し仕様。
///
/// @param[in] in_ptr_chunk_crit : 追加位置基準チャンク。
/// @param[in] in_version        : 追加するチャンクフォーマットのバージョン。
/// @param[in] in_type           : チャンク識別名。
/// @param[in] in_ptr_data       : 追加するチャンクのデータ部へのポインタ。
/// @param[in] in_data_size      : 追加するチャンクのデータ部のサイズ。
///
/// @return 追加されたチャンクを差すポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkBase*
  ChunkManager::AddChunkNext
    ( pin<ChunkBase> in_ptr_chunk_crit
    , U8             in_version
    , char_cptr      in_type
    , void_cptr      in_ptr_data
    , U64            in_data_size
    )
{
  auto ptr_chunklink = FindChunkLinker( in_ptr_chunk_crit );
  if( ptr_chunklink )
  {
    return
      zzf_addChunkXXX
        ( ptr_chunklink
        , in_version
        , in_type
        , in_ptr_data
        , in_data_size
        , &ChunkManager::AddChunkNext
        );
  }
  return nullptr;
}



// _____________________________________________________________________________
/// @brief
/// 指定されたチャンクの子供チャンクとして追加　※ チャンクリンカー渡し仕様。
///
/// @param[in] in_ptr_chunklink_crit : 追加位置基準チャンクに関連付けられているリンカー。
/// @param[in] in_ptr_chunk_add      : 追加データが設定されたチャンクへのポインタ。
///
/// @return 追加されたチャンクを差すポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkBase*
  ChunkManager::AddChunkChild
    ( ChunkLinkPtr   in_ptr_chunklink_crit
    , pin<ChunkBase> in_ptr_chunk_add
    )
{
  ChunkLinkPtr ptr_chunklink_child = in_ptr_chunklink_crit->GetChunkChild();
  if( ptr_chunklink_child ) // 子供をすでに保持している場合。
  {
    ptr_chunklink_child = ptr_chunklink_child->GetChunkEnd();
    return AddChunkNext( ptr_chunklink_child, in_ptr_chunk_add );
  }
  
  // 指定チャンクを保持しているかチェック。
  if( !IsChunkInclude( in_ptr_chunklink_crit ) )
  {
    return nullptr; // 保持していない場合。nullを返す。
  }

  // 
  ChunkBase* ptr_chunk_root = reinterpret_cast<ChunkBase*>(&m_buffer[0]);
  ChunkBase* ptr_chunk_crit = in_ptr_chunklink_crit->GetHeader();
  auto version = ptr_chunk_crit->GetVersion();

  // 対象のバイトオフセット。
  size_t target_byte_offs = (char_cptr)ptr_chunk_crit - (char_cptr)ptr_chunk_root;
  // ヘッダサイズ。
  size_t target_header_size =
    static_cast<size_t>(ptr_chunk_crit->GetHeaderSize());
  if( target_header_size == 0 )
  {
    return nullptr;
  }
  // 追加位置を計算。
  size_t insert_byte_offs = target_byte_offs + target_header_size;
  // 追加サイズ取得。
  SDEF   add_chunk_size = static_cast<SDEF>(in_ptr_chunk_add->GetSize());

  // データを追加する前に、変更の必要のあるチャンクの情報を書き換える。
  // ※ データを追加するとアドレスが変わる可能性があるため。
  zzf_adjustChunkSize_(
    in_ptr_chunklink_crit,
    add_chunk_size );

  // データを追加。
  // ※ この時点でバッファがアドレスが変わる可能性があるため、
  //    以後、チャンクへのリンクは再作成するまで一切無効とする。
  m_buffer.insert(
    m_buffer.begin() + insert_byte_offs,
    reinterpret_cast<char_cptr>(in_ptr_chunk_add),
    reinterpret_cast<char_cptr>(in_ptr_chunk_add) + add_chunk_size );

  // 
  ChunkBase* ptr_chunk_add =
    reinterpret_cast<ChunkBase*>(&m_buffer[insert_byte_offs]);

  switch( version )
  {
  case 0:
    // 追加されたチャンクのネクストフラグを設定。
    static_cast<ChunkInfo0*>(ptr_chunk_add)->SetNextFlag( false );
    // 追加対象の子供フラグをON.
    reinterpret_cast<ChunkInfo0*>(&m_buffer[target_byte_offs])->SetChildFlag( true );
    break;

  default:
    break;
  }

  // チャンクリンクの再構築。
  zzf_resetChunkLink();
  
  return ptr_chunk_add;
}


// _____________________________________________________________________________
/// @brief 指定されたチャンクの子供チャンクとして追加。
///
/// @param[in] in_ptr_chunk_crit : 基準となるチャンクポインタ。
/// @param[in] in_ptr_chunk_add  : データが設定されたチャンクデータへのポインタ。
///
/// @return 追加されたチャンクを差すポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkBase*
  ChunkManager::AddChunkChild
    ( pin<ChunkBase> in_ptr_chunk_crit
    , pin<ChunkBase> in_ptr_chunk_add
    )
{
  auto ptr_chunklink = FindChunkLinker( in_ptr_chunk_crit );
  if( ptr_chunklink )
  {
    return AddChunkChild( ptr_chunklink, in_ptr_chunk_add );
  }
  
  return nullptr;
}


// _____________________________________________________________________________
/// @brief 指定されたチャンクの子供チャンクとして追加　※ データ渡し仕様。
///
/// @param[in] in_ptr_chunklink_crit : 追加位置基準チャンクリンカー。
/// @param[in] in_version            : 追加するチャンクフォーマットのバージョン。
/// @param[in] in_type               : チャンク識別名。
/// @param[in] in_ptr_data           : 追加するチャンクのデータ部へのポインタ。
/// @param[in] in_data_size          : 追加するチャンクのデータ部のサイズ。
///
/// @return 追加されたチャンクを差すポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkBase*
  ChunkManager::AddChunkChild
    ( ChunkLinkPtr in_ptr_chunklink_crit
    , U8           in_version
    , char_cptr    in_type
    , void_cptr    in_ptr_data
    , U64          in_data_size
    )
{
  return
    zzf_addChunkXXX(
      in_ptr_chunklink_crit,
      in_version,
      in_type,
      in_ptr_data,
      in_data_size,
      &ChunkManager::AddChunkChild );
}


// _____________________________________________________________________________
/// @brief 指定されたチャンクの子供チャンクとして追加　※ データ渡し仕様。
///
/// @param[in] in_ptr_chunk_crit : 追加位置基準チャンク。
/// @param[in] in_version        : 追加するチャンクフォーマットのバージョン。
/// @param[in] in_type           : チャンク識別名。
/// @param[in] in_ptr_data       : 追加するチャンクのデータ部へのポインタ。
/// @param[in] in_data_size      : 追加するチャンクのデータ部のサイズ。
///
/// @return 追加されたチャンクを差すポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkBase*
  ChunkManager::AddChunkChild
    ( pin<ChunkBase> in_ptr_chunk_crit
    , _in<U8       > in_version
    , pin<char     > in_type
    , pin<void     > in_ptr_data
    , _in<U64      > in_data_size
    )
{
  auto ptr_chunklink = FindChunkLinker( in_ptr_chunk_crit );
  if( ptr_chunklink )
  {
    return
      zzf_addChunkXXX
        ( ptr_chunklink
        , in_version
        , in_type
        , in_ptr_data
        , in_data_size
        , &ChunkManager::AddChunkChild
        );
  }

  return nullptr;
}


// _____________________________________________________________________________
/// @brief チャンクの上書き。
///
/// @param[in] io_ptr_chunklink_target : 上書き対象のチャンクリンカーポインタ。
/// @param[in] in_ptr_chunk_src        : 上書きするデータのチャンクポインタ。
///
/// @return 上書きされたチャンクのポインタを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ChunkBase*
  ChunkManager::OverwriteChunk
    ( ChunkLinkPtr   io_ptr_chunklink_target
    , pin<ChunkBase> in_ptr_chunk_src
    )
{
  if( io_ptr_chunklink_target == nullptr  ||
      in_ptr_chunk_src        == nullptr )
  {
    return nullptr;
  }

  ChunkInfo0 const*
    ptr_chunk0_target =
      io_ptr_chunklink_target->GetHeaderT<ChunkInfo0>();

  auto   ptr_chunk_target = io_ptr_chunklink_target->GetHeader();
  size_t target_size    = static_cast<size_t>(ptr_chunk0_target->GetSize());
  size_t overwrite_size = static_cast<size_t>(in_ptr_chunk_src->GetSize());
  size_t
    target_byte_offs =
      (char_cptr)ptr_chunk_target - (char_cptr)GetChunkRoot()->GetHeader();

  // チャンクリンカーのリセットが必要か判定のため、現在のバッファポインタを保存。
  void* ptr_old = &m_buffer[0];
  // 次のチャンクの有無情報のみ受け継ぐので、一時保存。
  bool f_next = ptr_chunk0_target->IsChunkNext();

  // サイズ変更がある場合。
  if( target_size != overwrite_size )
  {
    // データを追加する前に、変更の必要のあるチャンクの情報を書き換える。
    // ※ データを追加するとアドレスが変わる可能性があるため。
    zzf_adjustChunkSize_
      ( io_ptr_chunklink_target
      , (int)overwrite_size - (int)target_size
      );

    // バッファの拡縮判定。
    if( target_size > overwrite_size ) // 上書きサイズが小さい場合。
    {
      // バッファ縮小。
      m_buffer.erase
        ( m_buffer.begin() + target_byte_offs + overwrite_size
        , m_buffer.begin() + target_byte_offs + target_size
        );
    }
    else
    if( target_size < overwrite_size ) // 上書きサイズが大きい場合。
    {
      // バッファ拡張。
      SDEF ex_size = overwrite_size - target_size;
      std::vector<char> tmp_buf( ex_size );
      m_buffer.insert
        ( m_buffer.begin() + target_byte_offs + target_size
        , tmp_buf.begin()
        , tmp_buf.end()
        );
    }
  }
  
  // コピー。
  CPL_MEMCPY
    ( &m_buffer[ target_byte_offs ]
    , in_ptr_chunk_src
    , overwrite_size
    );

  // 次のチャンクの有無情報を再設定。
  auto ptr_ow_chunk0 =
    reinterpret_cast<ChunkInfo0*>(&m_buffer[target_byte_offs]);
  ptr_ow_chunk0->SetNextFlag( f_next );

  if( ptr_old != &m_buffer[ 0 ] )
  {
    // チャンクリンクの再構築。
    zzf_resetChunkLink();
  }

  return ptr_ow_chunk0;
}


/// @cond
namespace zzz_ {

template<class T, class U>
void setupPrevChunk_for_DeleteChunk_( ChunkLinkerT<T,U>* in_ptr_chunklink )
{
  // もし削除対象がそのクラスの最後尾かつ前にチャンクがあれば、
  // 前のチャンクのネクストフラグをオフにする。
  if( !in_ptr_chunklink->IsChunkNext()  &&  in_ptr_chunklink->IsChunkPrev() )
  {
    ChunkBase* ptr_chunk = in_ptr_chunklink->GetChunkPrev()->GetHeader();
    switch( ptr_chunk->GetVersion() )
    {
    case 0:
      static_cast<ChunkInfo0*>(ptr_chunk)->SetNextFlag( false );
      break;
    }
  }
}

template<class T, class U>
void setupParentChunk_for_DeleteChunk_(
  ChunkLinkerT<T,U>* in_ptr_chunklink,
  size_t             in_erase_size )
{
  // もし削除対象の前後にチャンクが無い場合、親チャンクの子供フラグをオフにする。
  if( !in_ptr_chunklink->IsChunkPrev()  &&  !in_ptr_chunklink->IsChunkNext() )
  {
    auto ptr_chunklink_parent = in_ptr_chunklink->GetChunkParent();
    if( ptr_chunklink_parent )
    {
      switch( ptr_chunklink_parent->GetVersion() )
      {
      case 0:
        ptr_chunklink_parent->template GetHeaderT<ChunkInfo0>()->SetChildFlag( false );
        break;
      }
    }
  }

  // 削除対象を含むチャンクのサイズ調整。
  auto ptr_chunklink_upper = in_ptr_chunklink->GetChunkParent();
  while( ptr_chunklink_upper )
  {
    auto size = ptr_chunklink_upper->GetSize();
    ptr_chunklink_upper->SetSize( size - in_erase_size );

    // 上のチャンクへ。
    ptr_chunklink_upper = ptr_chunklink_upper->GetChunkParent();
  }
}


} // namespace zzz_
/// @endcond

// _____________________________________________________________________________
/// @brief チャンクの削除。
///
/// @param[in,out] io_ptr_chunklink_del : 削除対象のチャンクリンカーポインタ。
///
/// @retval true  : 成功
/// @retval false : 失敗
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ChunkManager::DeleteChunk( ChunkLinkPtr io_ptr_chunklink_del )
{
  //
  auto ptr_chunklink = m_up_chunklink.get();
  while( ptr_chunklink )
  {
    if( ptr_chunklink == io_ptr_chunklink_del )
    {
      // 削除サイズ。
      size_t erase_size = static_cast<size_t>(ptr_chunklink->GetSize());
      // アドレスオフセットを求める。
      size_t offs_target
        = (char_cptr)ptr_chunklink->GetHeader()
        - (char_cptr)GetChunkRoot()->GetHeader();

      // 前方のチャンクを設定する。
      zzz_::setupPrevChunk_for_DeleteChunk_( ptr_chunklink );

      // 親チャンクの調整。
      zzz_::setupParentChunk_for_DeleteChunk_( ptr_chunklink, erase_size );

      // バッファ削除
      m_buffer.erase(
        m_buffer.begin() + offs_target,
        m_buffer.begin() + offs_target + erase_size );
      
      // チャンクリンクの再構築。
      zzf_resetChunkLink();

      return true;
    }
    ptr_chunklink = ptr_chunklink->GetChunkNextAll();
  }

  return false;
}


} // namespace Core
} // namespace CPL
