/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2002/X/X - 2013/08/31
/// 
/// @brief  コンテナ　チャンクリンカー関連
/// 
/// ****************************************************************************

#include "../CPL_Core.h"

namespace CPL {
namespace Core {


// _____________________________________________________________________________
/// @brief コンストラクタ。
///
/// チャンクフォーマットポインタを解析し、ポインタツリーを構築する。
///
/// @param[in] in_ptr_chunk : チャンクフォーマットへのポインタ。
/// @throw std::bad_alloc : new失敗（内部new含む）
/// @throw CPL::Core::Exception<std::exception> : シグネチャ不正。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_ChunkPtr, class T_Derived>
ChunkLinkerT<T_ChunkPtr,T_Derived>::ChunkLinkerT( chunk_ptr_t in_ptr_chunk )
  : base_class( in_ptr_chunk ) // 自身のチャンクとなるポインタを設定ポインタ
{
  // 自身が管理することになるチャンクポインタのシグネチャチェック。
  // シグネチャが不正ならば例外を投げる。
  if( !in_ptr_chunk->IsValidSignature() )
  {
    Core::SStringA<512> msg( "Signature is invalid. : " );
    msg += typeid(*this).name();
    msg += "\n";
    CPL_THROW( msg );
  }

  // 子供リンク作成チェック。
  if( in_ptr_chunk->IsChunkChild() )
  {
    typedef typename Traits::RemovePointer<chunk_ptr_t>::type BaseType;
    static const bool f_const = Traits::IsConst< BaseType >::value;
    using ChunkType =
      typename Traits::IfType<f_const, Core::ChunkInfo0 const, Core::ChunkInfo0>::type;

    chunk_ptr_t ptr_chunk_child =
      reinterpret_cast<chunk_ptr_t>(
        static_cast<ChunkType*>(in_ptr_chunk)->GetDataAddr()
      );

    try {
      while( ptr_chunk_child )
      {
        this->AddNodeChild( CPL_NEW(derived_type)(ptr_chunk_child) );

        ptr_chunk_child = ptr_chunk_child->GetChunkNext();
      }
    }
    catch(...)
    {
      derived_ptr ptr_chunklink_child = GetChunkChild();
      if( ptr_chunklink_child )
      {
        DEL( ptr_chunklink_child, ptr_chunklink_child->GetChunkNext() );
      }
      throw;
    }
  }
}



// _____________________________________________________________________________
/// @brief デストラクタ。
///
/// 下位ノードを全て破棄する。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_ChunkPtr, class T_Derived>
ChunkLinkerT<T_ChunkPtr,T_Derived>::~ChunkLinkerT()
{
  derived_ptr ptr_chunklink_child = GetChunkChild();
  while( ptr_chunklink_child )
  {
    DEL( ptr_chunklink_child, ptr_chunklink_child->GetChunkNext() );
  }
}



// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 指定されたチャンクリンカー自身および、その全ての子供の中から、
// 指定されたチャンクポインタを保持するチャンクリンクを検索する汎用関数。
//  引数 :
//    in_ptr_this  : 処理対象となるチャンクリンカーのポインタ。
//    in_ptr_chunk : 比較するチャンクへのポインタ。
//  戻り値 :
//    該当したチャンクリンクポインタ。該当しなければnullptrを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_ChunkLinker, class T_Chunk>
T_ChunkLinker*
  zzf_ChunkLinker_FindChunkLinker
    ( T_ChunkLinker* in_ptr_this
    , T_Chunk*       in_ptr_chunk
    )
{
  // 渡されたチャンクと自身が保持するチャンクとのポインタ一致比較。
  if( in_ptr_chunk == in_ptr_this->GetHeader() )
  {
    return in_ptr_this;
  }

  // 子供等のチェック。
  auto ptr_chunklink_child = in_ptr_this->GetChunkChild();
  while( ptr_chunklink_child )
  {
    auto ret = ptr_chunklink_child->FindChunkLinker( in_ptr_chunk );
    if( ret )
    {
      return ret;
    }

    ptr_chunklink_child = ptr_chunklink_child->GetChunkChild();
  }

  return nullptr;
}


// _____________________________________________________________________________
/// @brief
/// 自身および全ての子供の中から、指定されたチャンクポインタを保持するチャンク
/// リンクを検索。
///
/// @param[in] in_ptr_chunk : 比較するチャンクへのポインタ。
///
/// @return 該当したチャンクリンクポインタ。該当しなければnullptrを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_ChunkPtr, class T_Derived>
T_Derived*
  ChunkLinkerT<T_ChunkPtr,T_Derived>::FindChunkLinker( chunk_cptr in_ptr_chunk )
{
  return
    zzf_ChunkLinker_FindChunkLinker(
      static_cast<derived_ptr>(this),
      in_ptr_chunk );
}


// _____________________________________________________________________________
/// @brief
/// 自身および全ての子供の中から、指定されたチャンクポインタを保持するチャンク
/// リンクを検索。
///
/// @param[in] in_ptr_chunk : 比較するチャンクへのポインタ。
///
/// @return  該当したチャンクリンクポインタ。該当しなければnullptrを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_ChunkPtr, class T_Derived>
T_Derived const*
  ChunkLinkerT<T_ChunkPtr,T_Derived>::FindChunkLinker( chunk_cptr in_ptr_chunk ) const
{
  return
    zzf_ChunkLinker_FindChunkLinker(
      static_cast<derived_cptr>(this),
      in_ptr_chunk );
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 指定されたチャンクリンカー自身と、そのチャンクリンカーが保持する全ての子供の
// 中から、指定されたチャンクデータと最初に一致するチャンクリンカーを検索する汎用関数。
//  引数 :
//    in_ptr_this      : 処理対象となるチャンクリンカーのポインタ。
//    in_chunk         : 比較データを格納したチャンクデータ。
//    in_matching_mask : マッチング対象の指定フラグ。
//  戻り値 :
//    最初に該当したチャンクリンクのポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_ChunkLinker, class T_Chunk>
T_ChunkLinker*
  zzf_ChunkLinker_FindChunkLinker
    ( T_ChunkLinker* in_ptr_this
    , T_Chunk const& in_chunk
    , U32            in_matching_mask
    )
{
  // 自身との比較（ver0用）
  if( in_ptr_this->GetHeader()->GetVersion() == 0 )
  {
    U32 flag = in_ptr_this->template GetHeaderT<ChunkInfo0 const>()->Compare( in_chunk );
    if( (flag & ChunkBase::eMatchingFlag::Version)  &&  (flag & in_matching_mask) )
    {
      return in_ptr_this;
    }
  }

  // 子供等から検索。
  auto ptr_chunklink_child = in_ptr_this->GetChunkChild();
  while( ptr_chunklink_child )
  {
    auto ret = ptr_chunklink_child->FindChunkLinker( in_chunk, in_matching_mask );
    if( ret )
    {
      return ret;
    }

    ptr_chunklink_child = ptr_chunklink_child->GetChunkNext();
  }

  return nullptr;
}


// _____________________________________________________________________________
/// @brief
/// 自身および全ての子供の中から指定されたチャンクデータと最初に一致するチャンク
/// リンカーを検索。
///
/// @param[in] in_chunk         : 比較データを格納したチャンクデータ。
/// @param[in] in_matching_mask : マッチング対象の指定フラグ。
///
/// @return 最初に該当したチャンクリンクのポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_ChunkPtr, class T_Derived>
T_Derived*
  ChunkLinkerT<T_ChunkPtr,T_Derived>::FindChunkLinker
    ( chunk_cref in_chunk
    , U32        in_matching_mask
    )
{
  return
    zzf_ChunkLinker_FindChunkLinker
      ( static_cast<derived_ptr>(this)
      , in_chunk
      , in_matching_mask
      );
}


// _____________________________________________________________________________
/// @brief 
/// 自身および全ての子供の中から指定されたチャンクデータと最初に一致するチャンク
/// リンカーを検索。
///
/// @param[in] in_chunk         : 比較データを格納したチャンクデータ。
/// @param[in] in_matching_mask : マッチング対象の指定フラグ。
///
/// @return 最初に該当したチャンクリンクのポインタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_ChunkPtr, class T_Derived>
T_Derived const*
  ChunkLinkerT<T_ChunkPtr,T_Derived>::FindChunkLinker
    ( chunk_cref in_chunk
    , U32        in_matching_mask
    ) const
{
  return
    zzf_ChunkLinker_FindChunkLinker
      ( static_cast<derived_cptr>(this)
      , in_chunk
      , in_matching_mask
      );
}


// _____________________________________________________________________________
/// @brief 
/// 指定されたチャンクリーンカー自身および、そのチャンクリンカーが保持する全ての
/// 子供の中から、指定されたタイプのチャンクとマッチングするチャンクリンカーを全
/// て検索する。
///
/// @param[in]  in_ptr_this
///   : 処理対象となるチャンクリンカーのポインタ。
/// @param[in]  in_chunk_data
///   : マッチング対象のチャンクデータ。※ 実チャンクである必要は無い。
/// @param[in]  in_matching_mask
///   : マッチングの際に比較項目を選択するためのマスクフラグ。
/// @param[out] out_list
///   : 検索されたチャンクのポインタを追加する配列クラスへの参照。
///
/// @return 該当したチャンク数を返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_ChunkLinker, class T_Chunk>
int
  zzf_ChunkLinker_FindChunkLinker
    ( T_ChunkLinker*               in_ptr_this
    , T_Chunk const&               in_chunk_data
    , U32                          in_matching_mask
    , std::vector<T_ChunkLinker*>& out_list
    )
{
  U32 flag = 0;
  // バージョンチェック
  switch( in_ptr_this->GetHeader()->GetVersion() )
  {
  case 0: // version 0（ChunkInfo0形式による比較）
    flag = in_ptr_this->template GetHeaderT<ChunkInfo0 const>()->Compare( in_chunk_data );
    if( (flag & ChunkBase::eMatchingFlag::Version) ) // バージョン合致チェック。
    {
      if( (flag & in_matching_mask) )
      {
        out_list.push_back( in_ptr_this );
      }
    }
    break;

  default: // 未対応バージョン。基本チャンクのみ比較する。
    flag = in_ptr_this->GetHeader()->Compare( in_chunk_data );
    if( flag & in_matching_mask )
    {
      out_list.push_back( in_ptr_this );
    }
    break;
  }

  // 子供等のチェック。
  auto ptr_chunklink_child = in_ptr_this->GetChunkChild();
  while( ptr_chunklink_child )
  {
    ptr_chunklink_child->FindChunkLinker( in_chunk_data, in_matching_mask, out_list );
    ptr_chunklink_child = ptr_chunklink_child->GetChunkNext();
  }

  return static_cast<int>(out_list.size());
}


// _____________________________________________________________________________
/// @brief 自身および全ての子供の中から指定されたタイプのチャンクを全て検索する。
///
/// @param[in] in_chunk_data
///   : マッチング対象のチャンクデータ。※ 実チャンクである必要は無い。
/// @param[in] in_matching_mask
///   : マッチングの際に比較項目を選択するためのマスクフラグ。
/// @param[out] out_list
///   : 検索されたチャンクのポインタを追加する配列クラスへの参照。
///
/// @return 該当したチャンク数を返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_ChunkPtr, class T_Derived>
int
  ChunkLinkerT<T_ChunkPtr,T_Derived>::FindChunkLinker
    ( chunk_cref                in_chunk_data
    , U32                       in_matching_mask
    , std::vector<derived_ptr>& out_list
    )
{
  return
    zzf_ChunkLinker_FindChunkLinker
      ( static_cast<derived_ptr>(this)
      , in_chunk_data
      , in_matching_mask
      , out_list
      );
}


// _____________________________________________________________________________
/// @brief 自身および全ての子供の中から指定されたタイプのチャンクを全て検索する。
///
/// @param[in] in_chunk_data
///   : マッチング対象のチャンクデータ。※ 実チャンクである必要は無い。
/// @param[in] in_matching_mask
///   : マッチングの際に比較項目を選択するためのマスクフラグ。
/// @param[out] out_list
///   : 検索されたチャンクのポインタを追加する配列クラスへの参照。
///
/// @return 該当したチャンク数を返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_ChunkPtr, class T_Derived>
int
  ChunkLinkerT<T_ChunkPtr,T_Derived>::FindChunkLinker
    ( chunk_cref                 in_chunk_data
    , U32                        in_matching_mask
    , std::vector<derived_cptr>& out_list
    ) const
{
  return
    zzf_ChunkLinker_FindChunkLinker
      ( static_cast<derived_cptr>(this)
      , in_chunk_data
      , in_matching_mask
      , out_list
      );
}


// _____________________________________________________________________________
/// @brief
/// 自身および全ての子供の中から指定されたチャンクが含まれるかどうかをチェック。
///
/// @param[in] in_ptr_chunk
///   : 比較対象のチャンクリンククラスのポインタ。
///
/// @retval true  : 該当。
/// @retval false : 該当無し。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_ChunkPtr, class T_Derived>
bool ChunkLinkerT<T_ChunkPtr,T_Derived>::IsChunkInclude( chunk_cptr in_ptr_chunk ) const
{
  if( this->GetHeader() == in_ptr_chunk )
  {
    return true;
  }
  auto ptr_chunklink_child = this->GetChunkChild();
  while( ptr_chunklink_child )
  {
    if( ptr_chunklink_child->IsChunkInclude( in_ptr_chunk ) )
    {
      return true;
    }
    ptr_chunklink_child = ptr_chunklink_child->GetChunkNext();
  }

  return false;
}


// _____________________________________________________________________________
/// @brief
/// 自身および全ての子供の中から、指定されたチャンクリンカーが保持するチャンクが
/// 含まれるかどうかをチェック。
///
/// @param[in] in_ptr_chunklink
///   : 比較対象のチャンクリンククラスのポインタ。
///
/// @retval true  : 該当。
/// @retval false : 該当無し。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_ChunkPtr, class T_Derived>
bool
  ChunkLinkerT<T_ChunkPtr,T_Derived>::IsChunkInclude(
    derived_cptr in_ptr_chunklink ) const
{
  return IsChunkInclude( in_ptr_chunklink->GetHeader() );
}


// _____________________________________________________________________________
/// @brief 保持する子供チャンクの個数を取得（自身は含まない）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_ChunkPtr, class T_Derived>
U32 ChunkLinkerT<T_ChunkPtr,T_Derived>::GetChildChunkNum() const
{
  U32 num = 0;
  auto ptr_chunklink_child = GetChunkChild();
  while( ptr_chunklink_child )
  {
    num += ptr_chunklink_child->GetChildChunkNum() + 1;
    ptr_chunklink_child = ptr_chunklink_child->GetChunkNext();
  }
  return num;
}



// _____________________________________________________________________________
/// @brief リンク先のチャンクの初期化関数を呼び出す。
///
/// @param[in] in_type       : チャンクタイプ識別文字列。
/// @param[in] in_flag_next  : 次のチャンクがあるかどうか。
/// @param[in] in_flag_child : 子供を持つかどうか。
/// @param[in] in_data_size  : データサイズ。※ 最大値はバージョンにより違うので注意。
/// 
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  ChunkLinker::Initialize
    ( char_cptr in_type
    , bool      in_flag_next
    , bool      in_flag_child
    , U64       in_data_size
    )
{
  if( get() )
  {
    static_cast<ChunkInfo0*>(*get())->Initialize
      ( in_type
      , in_flag_next
      , in_flag_child
      , in_data_size
      );
  }
}





template class ChunkLinkerT<ChunkBase*, ChunkLinker>;
template class ChunkLinkerT<ChunkBase const*, ChunkLinkerConst>;


} // namespace Core
} // namespace CPL
