/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/11/30
/// 
/// @brief  DirectX 頂点要素関連
/// 
/// ****************************************************************************


#include "CPL_DX_Layout_Buffer.h"


namespace CPL {
namespace DX {
//namespace Graph {
namespace Layout {


// _____________________________________________________________________________
/// @brief コンストラクタ（引数リストversion）
/// 
/// @param[in] in_elements : 要素リストオブジェクトを指定します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Buffer::Buffer( cref<Elements> in_elements )
{
  // サイズテーブル０クリア
  ::memset( m_elements_size, 0, sizeof(m_elements_size) );

  // エレメントのテーブルインスタンスを作成
  const size_t element_num = in_elements.GetElementNum();
  m_element_table.Resize( element_num );
  CPL_LOOP2( element_num, element_idx )
  {
    m_element_table[ element_idx ] =
      in_elements.GetElement( element_idx );

    const int slot = m_element_table[ element_idx ].InputSlot;
    if( slot >= 0 )
    {
      const int size = m_element_table[ element_idx ].GetSize_for_Format();
      if( size > 0 )
      {
        m_elements_size[ slot ] += size;
      }
    }
  }
}


// _____________________________________________________________________________
/// @brief コンストラクタ（DirectXエレメントテーブルバージョン）
/// 
/// @param[in] in_element_table : 頂点要素配列の先頭アドレスを指定します。
/// @param[in] in_element_num : 頂点要素配列の要素数を指定します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Buffer::Buffer
( DX_IN_ELEMENT_DESC const in_element_table[]
, _in<size_t>              in_element_num
)
{
  // サイズテーブル０クリア
  ::memset( m_elements_size, 0, sizeof(m_elements_size) );

  //
  CPL_LOOP2( in_element_num, element_idx )
  {
    m_element_table.AddEnd( in_element_table[ element_idx ] );

    const int slot = m_element_table[ element_idx ].InputSlot;
    if( slot >= 0 )
    {
      const int size = m_element_table[ element_idx ].GetSize_for_Format();
      if( size > 0 )
      {
        m_elements_size[ slot ] += size;
      }
    }
  }
}


// _____________________________________________________________________________
/// @brief コピーコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Buffer::Buffer( cref<Buffer> in_layout_buf )
{
  m_element_table = in_layout_buf.m_element_table;
  ::memcpy
    ( m_elements_size
    , in_layout_buf.m_elements_size
    , sizeof(m_elements_size)
    );
}


// _____________________________________________________________________________
/// @brief 入力レイアウトの作成
/// 
/// @param[in] in_ptr_d3d_shader_compiled
///   : コンパイル済みシェーダーのバッファアドレスを指定します。
/// @param[out] out_pp_input_layout
///   : 取得するD3DInputLayoutのポインタのアドレスを指定します。
/// 
/// @return DirectXのリターンコード(HRESULT型)を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT
  Buffer::CreateInputLayout
    ( ID3DBlob*        in_ptr_d3d_shader_compiled
    , DXInputLayout**  out_pp_input_layout
    ) const
{
  CPL_DBG_OUTPUT_RETURN_VALUE_IF
    ( !in_ptr_d3d_shader_compiled
    , E_INVALIDARG
    , SwS("ShaderBuffer(Blob) is invalid.\n\t - CPL::DX::Layout::Buffer::CreateInputLayout()\n")
    );
  return
    Graphics_i_::GetDevice()->CreateInputLayout
      ( &m_element_table[0]
      , static_cast<UINT>(m_element_table.GetNum())
      , in_ptr_d3d_shader_compiled->GetBufferPointer()
      , in_ptr_d3d_shader_compiled->GetBufferSize()
      , out_pp_input_layout
      );
}


// _____________________________________________________________________________
/// @brief 指定したCorePLi頂点フォーマットの要素インデックスの取得
/// 
/// @param[in] in_vtx_format
///   : 頂点フォーマットのフォーマットIDを指定します。\n
///   : 詳しくはCPL::DX::Vtx::eTypeIDを参照してください。
/// 
/// @return
/// 該当した頂点フォーマットの要素インデックスを返します。\n
/// 該当しない場合は-1を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int Buffer::GetElementIndex( _in<Vtx::eTypeID> in_vtx_format ) const
{
  const int tbl_num = static_cast<int>(m_element_table.GetNum());

  CPL_LOOP2( tbl_num, idx )
  {
    if( m_element_table[ idx ].Format == in_vtx_format )
    {
      return idx;
    }
  }

  return -1;
}


// _____________________________________________________________________________
/// @brief 要素サイズの取得（要素インデックス指定バージョン）
/// 
/// @param[in] in_element_idx
///   : 頂点要素インデックスを指定します。
/// 
/// @return 指定された頂点要素インデックスの要素サイズを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int Buffer::GetElementSize( _in<int> in_element_idx ) const
{
  if( in_element_idx >= 0  &&  GetElementNum() > 0 )
  {
    return m_element_table[ in_element_idx ].GetSize_for_Format();
  }
  else
  {
    return 0;
  }
}


// _____________________________________________________________________________
/// @brief 要素サイズの取得（CorePLi頂点フォーマット指定バージョン）
/// 
/// CorePLiで定義されている要素識別コードであるCPL::DX::Vtx::eTypeIDを指定して、
/// それに対応する要素のサイズを取得します。
/// 
/// @param[in] in_vtx_format
///   : 頂点要素フォーマットを指定します。\n
///   : 
/// 
/// @return 指定されたフォーマットの頂点要素サイズを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int Buffer::GetElementSize( _in<Vtx::eTypeID> in_vtx_format ) const
{
  return GetElementSize( GetElementIndex( in_vtx_format ) );
}


// _____________________________________________________________________________
/// @brief 指定した要素のオフセットの取得
/// 
/// @param[in] in_element_idx
///   : オフセットを取得したい要素のインデックスを指定します。
/// 
/// @return
/// 指定した要素のオフセット値をバイト単位で返します。\n
/// インデックスが範囲外の場合、INVALID_INDEX32を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int Buffer::GetElementOffs( _in<int> in_element_idx ) const
{
  if( 0 <= in_element_idx
   && in_element_idx < static_cast<int>(GetElementNum()) )
  {
    return m_element_table[ in_element_idx ].AlignedByteOffset;
  }

  return INVALID_INDEX32;
}


// _____________________________________________________________________________
/// @brief 指定したCorePLi頂点フォーマットに対応する要素のオフセットの取得
/// 
/// @param[in] in_vtx_format : CorePLi頂点フォーマットを指定します。
/// 
/// @return
/// 指定したフォーマットのオフセット値をバイト単位で返します。\n
/// インデックスが範囲外の場合、INVALID_INDEX32を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int Buffer::GetElementOffs( _in<Vtx::eTypeID> in_vtx_format ) const
{
  return GetElementOffs( GetElementIndex( in_vtx_format ) );
}


// _____________________________________________________________________________
/// @brief バッファクリア
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Buffer::Clear()
{
  m_element_table.Release();
  ::memset( m_elements_size, 0, sizeof(m_elements_size) );
}


// _____________________________________________________________________________
/// @brief 頂点要素の追加
/// 
/// @param[in] in_element : 追加する要素データを指定ます。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Buffer::AddElement( _in<Element> in_element )
{
  m_element_table.AddEnd( in_element );
}


// _____________________________________________________________________________
/// @brief フォーマットに対応する頂点要素の取得
/// 
/// @param[in] in_vtx_format
///   : 取得したい要素の頂点フォーマットを指定します。\n
///   : 詳しくはCPL::DX::Vtx::eTypeIDを参照してください。
/// 
/// @return
/// フォーマットに対応する頂点要素のポインタを返します。\n
/// 該当しない場合はnullptrを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cptr<Element>
  Buffer::GetElement_by_Format( _in<Vtx::eTypeID> in_vtx_format ) const
{
  ElementTable::const_iterator itr_ve  = m_element_table.begin();
  ElementTable::const_iterator itr_end = m_element_table.end();

  while( itr_ve != itr_end )
  {
    if( itr_ve->Format == in_vtx_format )
    {
      return &*itr_ve;
    }

    ++itr_ve;
  }

  return nullptr;
}


// _____________________________________________________________________________
/// @brief コピー
/// 
/// @return 自身の参照を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Buffer&
  Buffer::operator = ( cref<Buffer> in_vsl )
{
  m_element_table = in_vsl.m_element_table;

  return *this;
}




} // namespace Layout
//} // namespace Graph
} // namespace CPL
} // namespace DX

