/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/12/23
/// 
/// @brief  メッシュ　ボーン関連
/// 
/// ****************************************************************************


#include "CPL_CDI_MeshData.h"

namespace CPL {
namespace CDI {


// _____________________________________________________________________________
/// @brief デフォルトコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MeshData::Bone::Bone()
  : m_ptr_world_mtx( nullptr )
{
}


// _____________________________________________________________________________
/// @brief コンストラクタ（属するオブジェクト指定version）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MeshData::Bone::Bone( _in<sdptr<Object>> in_sp_object )
  : m_ptr_world_mtx( nullptr )
  , m_wp_belong_object( in_sp_object )
{
}


// _____________________________________________________________________________
/// @brief コピーコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MeshData::Bone::Bone( cref<Bone> in_bone )
  : td_base( in_bone )
  , m_ptr_world_mtx( nullptr )
  , m_wp_belong_object( in_bone.m_wp_belong_object )
  , m_vec_pos( in_bone.m_vec_pos )
  , m_vec_rot( in_bone.m_vec_rot )
  , m_vec_scl( in_bone.m_vec_scl )
{
}


// _____________________________________________________________________________
/// @brief ムーブコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MeshData::Bone::Bone( rvref<Bone> in_bone )
  : td_base( std::move(in_bone) )
  , m_ptr_world_mtx( nullptr )
  , m_wp_belong_object( std::move(in_bone.m_wp_belong_object) )
  , m_vec_pos( in_bone.m_vec_pos )
  , m_vec_rot( in_bone.m_vec_rot )
  , m_vec_scl( in_bone.m_vec_scl )
  , m_wp_relation_keylist_pos( std::move(in_bone.m_wp_relation_keylist_pos) )
  , m_wp_relation_keylist_rot( std::move(in_bone.m_wp_relation_keylist_rot) )
  , m_wp_relation_keylist_scl( std::move(in_bone.m_wp_relation_keylist_scl) )
{
}


// _____________________________________________________________________________
/// @copydoc doxycomment::func_zzf_setupData_by_File_
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  MeshData::Bone::zzf_setupData_by_File_
    ( pin<MeshFileInfo::Chunk> in_ptr_chunk
    , rio<Array_DataSPtr     > io_table_for_data_pointer
    , rio<Array_ChunkPtr     > io_table_for_chunk_pointer
    )
{
  char_cptr ptr_data_addr = reinterpret_cast<char_cptr>(in_ptr_chunk);
  MFI_Bone const* ptr_info_bone =
    reinterpret_cast<MFI_Bone const*>(ptr_data_addr);

  // データポインタ登録
  io_table_for_data_pointer.Add( ptr_info_bone->m_GID, this->GetLinkMyself() );
  // チャンクポインタ登録
  io_table_for_chunk_pointer.Add( reinterpret_cast<void*>(this), in_ptr_chunk );

  // データ設定
  m_vec_pos = ptr_info_bone->m_vec_pos;
  m_vec_rot = ptr_info_bone->m_vec_rot;
  m_vec_scl = ptr_info_bone->m_vec_scl;
}


// _____________________________________________________________________________
/// @copydoc doxycomment::func_zzf_setupRelation_
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  MeshData::Bone::zzf_setupRelation_
    ( rio<Array_DataSPtr> io_table_for_data_pointer
    , rio<Array_ChunkPtr> io_table_for_chunk_pointer
    )
{
  //
  const MeshFileInfo::Chunk** pp_chunk = io_table_for_chunk_pointer.Find( this );
  if( !pp_chunk  ||  !(*pp_chunk) )
  {
    return;
  }

  // 次のボーン
  MFI_Bone const* ptr_info_bone = static_cast<MFI_Bone const*>(*pp_chunk);
  void_sptr* psp_data = io_table_for_data_pointer.Find( ptr_info_bone->m_GID_next );
  sdptr<Bone>
    sp_bone_next =
      psp_data ? std::static_pointer_cast<Bone>(*psp_data)
               : nullptr;
  if( sp_bone_next )
  {
    AddLinkNext( sp_bone_next );
  }

  // 子供ボーン
  psp_data = io_table_for_data_pointer.Find( ptr_info_bone->m_GID_child );
  sdptr<Bone>
    sp_bone_child =
      psp_data ? std::static_pointer_cast<Bone>(*psp_data)
               : nullptr;
  if( sp_bone_child )
  {
    AddLinkChild( sp_bone_child );
  }

  // 関連付け頂点グループ

  /* ↓はアニメーションで関連づけられる
  // 座標キーリスト
  m_wp_relation_keylist_pos = io_table_for_data_pointer.Find( this->m_ptr_info_bone->m_GID_keylist_pos );
  // 角度キーリスト
  m_wp_relation_keylist_rot = io_table_for_data_pointer.Find( this->m_ptr_info_bone->m_GID_keylist_rot );
  // スケールキーリスト
  m_wp_relation_keylist_scl = io_table_for_data_pointer.Find( this->m_ptr_info_bone->m_GID_keylist_scl );
  */

//  this->m_ptr_info_bone = nullptr; // ファイルデータへのポインタをクリア
}

//// _____________________________________________________________________________
//// 自身のクラスのノード数を取得
//// _____________________________________________________________________________
//int MeshData::Bone::GetNodeNum() const
//{
//  ScPtr_Bone sp_bone = this->getThisSmPtr();
//  int count = 0;
//
//  while( sp_bone )
//  {
//    ++count;
//    sp_bone = sp_bone->m_sp_bone_next;
//  }
//
//  return count;
//}


// _____________________________________________________________________________
/// @brief ローカル座標の取得
/// 
/// @copydetails doxycomment::arg_in_flag_interpolate
/// @param[out] out_vec : 座標格納用のベクトル変数を渡します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  MeshData::Bone::GetLocalPos
    ( _in <bool    > in_flag_interpolate
    , rout<CVector3> out_vec
    ) const
{
  if( auto sp_keylist = this->m_wp_relation_keylist_pos.lock() )
  {
    zzf_getKeyListVec
      ( sp_keylist
      , in_flag_interpolate
      , out_vec
      );

    out_vec += m_vec_pos;
  }
  else
  {
    out_vec = m_vec_pos;
  }
}


// _____________________________________________________________________________
/// @brief ローカル角度の取得
/// 
/// @copydetails doxycomment::arg_in_flag_interpolate
/// @param[out] out_vec : 角度格納用のベクトル変数を渡します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  MeshData::Bone::GetLocalRot
    ( _in <bool    > in_flag_interpolate
    , rout<CVector3> out_vec
    ) const
{
  if( auto sp_keylist = this->m_wp_relation_keylist_rot.lock() )
  {
    zzf_getKeyListVec
      ( sp_keylist
      , in_flag_interpolate
      , out_vec
      );

    out_vec += m_vec_rot;
  }
  else
  {
    out_vec += m_vec_rot;
  }
}


// _____________________________________________________________________________
/// @brief ローカルスケールの取得
/// 
/// @copydetails doxycomment::arg_in_flag_interpolate
/// @param[out] out_vec : スケール格納用のベクトル変数を渡します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  MeshData::Bone::GetLocalScl
    ( _in <bool    > in_flag_interpolate
    , rout<CVector3> out_vec
    ) const
{
  if( auto sp_keylist = this->m_wp_relation_keylist_scl.lock() )
  {
    zzf_getKeyListVec
      ( sp_keylist
      , in_flag_interpolate
      , out_vec
      );

    out_vec *= m_vec_scl;
  }
  else
  {
    out_vec = m_vec_scl;
  }
}


// _____________________________________________________________________________
/// @brief 後片付け
/// 
/// @param[in] io_table_for_data_pointer
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void MeshData::Bone::Release( rio<Array_DataSPtr> io_table_for_data_pointer )
{
  this->Detach();
  this->m_wp_relation_keylist_pos.reset();
  this->m_wp_relation_keylist_rot.reset();
  this->m_wp_relation_keylist_scl.reset();
}


// _____________________________________________________________________________
/// @brief マトリクス更新処理
/// 
/// @param[out] out_ptr_matrix
///   : 取得用のマトリクスオブジェクトのポインタを渡します。\n
///   : 基本的には属するオブジェクトのマトリクス配列を渡します。\n
///   : 渡される配列はボーン最大数を渡さなければいけません。
/// @param[in] in_ptr_matrix_for_parent
///   : 親マトリクスを指すポインタを指定します。
/// @copydetails doxycomment::arg_in_flag_interpolate
/// 
/// @return 
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ptr<CMatrix>
  MeshData::Bone::UpdateMatrix
    ( pout<CMatrix> out_ptr_matrix
    , pin <CMatrix> in_ptr_matrix_for_parent
    , _in <bool   > in_flag_interpolate
    )
{
  auto ptr_matrix = out_ptr_matrix;
  m_ptr_world_mtx = ptr_matrix;
  ++ptr_matrix;

  CVector3 vscl, vrot, vpos;

  GetLocalScl( in_flag_interpolate, vscl );    // スケール
  GetLocalRot( in_flag_interpolate, vrot );   // 角度
  GetLocalPos( in_flag_interpolate, vscl ); // 座標
  
  m_ptr_world_mtx->Identity();
  m_ptr_world_mtx->Scaling( vscl );
  m_ptr_world_mtx->RotYrad( vrot.y );
  m_ptr_world_mtx->RotXrad( vrot.x );
  m_ptr_world_mtx->RotZrad( vrot.z );
  m_ptr_world_mtx->Translate( vpos );

  // 親マトリクスの乗算
  if( in_ptr_matrix_for_parent )
  {
    *m_ptr_world_mtx *= *in_ptr_matrix_for_parent;
  }

  if( auto sp_bone_child = this->GetLinkChild() )
  {
    ptr_matrix =
      sp_bone_child->UpdateMatrix
        ( ptr_matrix
        , m_ptr_world_mtx
        , in_flag_interpolate
        );
  }

  return ptr_matrix;
}





} // namespace CDI
} // namespace CPL