/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2014/01/05 - 2017/01/11
/// 
/// @brief  チャンクコンテナクラス vertion 0 関連
/// 
/// ※ 2014/01/05 CPL_Core_ChunkBase.cppから分離。
/// 
/// ****************************************************************************

#include "../CPL_Core.h"

namespace CPL {
namespace Core {





// _____________________________________________________________________________
/// @brief 初期化。
///
/// @param[in] in_type       : チャンクタイプ識別文字列。
/// @param[in] in_flag_next  : 次のチャンクがあるかどうか。
/// @param[in] in_flag_child : 子供を持つかどうか。
/// @param[in] in_data_size  : データサイズ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  ChunkInfo0::Initialize
    ( pin<char> in_type
    , _in<bool> in_flag_next
    , _in<bool> in_flag_child
    , _in<U64 > in_data_size
    )
{
  memcpy( signature, CPL_CHUNK_SIGNATURE, sizeof(signature) );
  version   = 0;
  f_next    = in_flag_next;
  f_child   = in_flag_child;
  size      = (CalculateBufferSize( in_type, in_data_size )+7) / 8;
  SetType( in_type );
}

// _____________________________________________________________________________
/// @brief チャンクデータの一致フラグを取得。
///
/// @param[in] in_chunk : 比較するチャンクデータへの参照。
///
/// 32bit整数が返る。\n
/// ※ マッチングフラグとの論理積をとることで、一致項目がわかる。\n
/// 　 詳細はChunkBase::MATCHING_V0_を参照。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U32 ChunkInfo0::Compare( cref<ChunkBase> in_chunk ) const
{
  // シグネチャとバージョン。
  U32 flag = ChunkBase::Compare( in_chunk );
  if( !(flag & eMatchingFlag::Signature) || !(flag & eMatchingFlag::Version) )
  {
    return flag;
  }

  ChunkInfo0 const& in_chunk0 = static_cast<ChunkInfo0 const&>(in_chunk);

  if( size    == in_chunk0.size    ){ flag |= eMatchingFlag::Size;  }
  if( f_next  == in_chunk0.f_next  ){ flag |= eMatchingFlag::Next;  }
  if( f_child == in_chunk0.f_child ){ flag |= eMatchingFlag::Child; }

  if( CheckType( in_chunk0.GetTypeAddr() ) )
  {
    flag |= eMatchingFlag::TypeAll;
  }
  int this_type_num  = GetTypeElementNum();
  int check_type_num = in_chunk0.GetTypeElementNum();
  int num = Min( this_type_num, check_type_num );
  //CPL_LOOP2( num, idx )
  for( int idx = 0; idx < num; ++idx )
  {
    char tmp_type[ TYPE_ELEMENT_MAX ] = { 0 };
    in_chunk0.GetType( idx, tmp_type, sizeof(tmp_type) );
    if( CheckType( idx, tmp_type ) )
    {
      flag |= eMatchingFlag::TypeOr;
      flag |= (eMatchingFlag::Type0 << idx);
    }
  }

  return flag;
}


// _____________________________________________________________________________
/// @brief チャンクタイプの取得。
///
/// @param[out] out_str     : 文字列を格納するバッファのポインタ。
/// @param[in]  in_buf_size : out_strのバッファサイズ。
///
/// @return タイプが設定されていればtrueを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ChunkInfo0::GetType( pout<char> out_str, _in<U32> in_buf_size ) const
{
  if( type_length == 0 )
    return false;

  int copy_num = Min<int>( type_length, in_buf_size-1 );
  memcpy( out_str, GetTypeAddr(), copy_num );
  out_str[ copy_num ] = 0;

  return true;
}


// _____________________________________________________________________________
/// @brief チャンクタイプの取得（要素指定付き）
///
/// @param[in]  in_idx      : 要素インデックス。範囲は0 ～ 要素数-1。
/// @param[out] out_str     : 文字列を格納するバッファのポインタ。
/// @param[in]  in_buf_size : out_strのバッファサイズ。
///
/// @return インデックスが範囲内ならばtrue, 範囲外ならばfalseを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  ChunkInfo0::GetType
    ( _in <U32 > in_idx
    , pout<char> out_str
    , _in <U32 > in_buf_size
    ) const
{
  // プチ補足。
  // ここで扱う文字列はnull終端ではないため、各種補助関数は使えない。

  int length = type_length;
  if( length == 0 )
    return false;

  char_cptr type_    = GetTypeAddr();
  char_cptr type_end = type_ + length; // 終端+1を指す。
  char_cptr ptr_C    = type_;
  U32 N = 0;
  while( ptr_C < type_end )
  {
    if( N == in_idx )
    {
      // コピー
      //out_str.resize( length + 1, 0 );
      char_ptr dest = out_str;
      //
      while( ptr_C < type_end )
      {
        if( *ptr_C == '/' ) // 次のセパレータがあれば抜ける。
        {
          *dest = 0;
          return true;
        }
        *dest = *ptr_C;
        ++dest;
        ++ptr_C;
      }
      *dest = 0;
      return true;
    }

    if( *ptr_C == '/' )
    {
      ++N;
    }
    ++ptr_C;
  }

  return false;
}


// _____________________________________________________________________________
/// @brief チャンクタイプ用素数の取得。
///
/// @return 
/// セパレータ数＋１を返す。
/// ただし、文字列が無い場合は０を返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int ChunkInfo0::GetTypeElementNum() const
{
  // プチ補足。
  // ここで扱う文字列はnull終端ではないため、各種補助関数は使えない。

  size_t length = type_length;
  if( length == 0 )
    return 0;

  int count = 1;
  char_cptr ptr_C = GetTypeAddr();
  //CPL_LOOP2( length, idx )
  for( size_t idx = 0; idx < length; ++idx )
  {
    if( *ptr_C == '/' ) ++count;
    ++ptr_C;
  }

  return count;
}


// _____________________________________________________________________________
/// @brief チャンクタイプ設定。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ChunkInfo0::SetType( pin<char> in_type )
{
  type_length = static_cast<U16>( strlen( in_type ) );
  memcpy( GetTypeAddr(), in_type, type_length );
}


// _____________________________________________________________________________
/// @brief 分割要素指定つきタイプチェック。
///
/// @param[in] in_idx  : チェックする要素のインデックス。
/// @param[in] in_type : 比較文字列。
///
/// @return 一致した場合trueを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ChunkInfo0::CheckType( _in<U32> in_idx, pin<char> in_type ) const
{
  char tmp_type[ TYPE_ELEMENT_MAX ] = { 0 };
  if( GetType( in_idx, tmp_type, sizeof(tmp_type) ) )
  {
    return ::strcmp( tmp_type, in_type ) == 0;
  }
  return false;
}


// _____________________________________________________________________________
/// @brief ヘッダサイズの計算。
///
/// @param[in] in_type : チャンクタイプ名へのポインタ。
///
/// @return ヘッダサイズ。※ サイズは8byteでアラインされる。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U32 ChunkInfo0::ComputeHeaderSize( pin<char> in_type )
{
  U32 len = (U32)strlen( in_type ) + 1;
  return AlignHigh<int>( (U32)sizeof(ChunkInfo0)-2 + len, 8 );
}


// _____________________________________________________________________________
/// @brief 必要なバッファサイズの計算。
///
/// @param[in] in_type      : チャンクタイプ名へのポインタ。
/// @param[in] in_data_size : データサイズ（最大2^31）
///
/// @return バッファサイズ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64 ChunkInfo0::CalculateBufferSize( pin<char> in_type, _in<U64> in_data_size )
{
  return
    ComputeHeaderSize( in_type ) + // ヘッダサイズは8byteアラインされている。
    AlignHigh<U64>( in_data_size, 8 );
}




} // namespace Core
} // namespace CPL
