/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/12/02
/// 
/// @brief  拡張可能バッファ関連
/// 
/// ****************************************************************************

#include "CPL_DX_Buffer.h"
#include "CPL_DX_Graphics.h"


namespace CPL {
namespace DX {
//namespace Graph {
namespace Buffer {


// _____________________________________________________________________________
/// @brief コンストラクタ（バッファの生成あり）
/// 
/// @param[in] in_element_size    : １要素のサイズ（バイト単位）を指定します。
/// @param[in] in_element_num     : 要素数を指定します。
/// @param[in] in_flags_bind
///   : パイプライン ステージへのバインドに関するフラグです。\n
///   : 詳しくはDirectX BindFlags等で検索してください。 
/// @param[in] in_ptr_init_data   : 初期化データを指すアドレスをしてします。
/// @param[in] in_slot_No_default : デフォルトのスロットNoを指定します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DynamicEX::DynamicEX
( _in<size_t> in_element_size
, _in<size_t> in_element_num
, _in<UINT  > in_flags_bind
, pin<void  > in_ptr_init_data
, _in<UINT  > in_slot_No_default
) : Dynamic( in_slot_No_default )
  , m_flags_bind( (U16)in_flags_bind )
  , m_flag_change( false )
  , m_flag_resize( false )
{
  Create( in_element_size, in_element_num, in_ptr_init_data );
}


// _____________________________________________________________________________
/// @brief デストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DynamicEX::~DynamicEX()
{
}


// _____________________________________________________________________________
/// @brief バッファの生成
/// 
/// @param[in] in_element_size : １要素のサイズ（バイト単位）を指定します。
/// @param[in] in_element_num  : 要素数を指定します。
/// @param[in] in_ptr_init_data
///   : 初期化用のデータを指すアドレスを指定します。\n
///   : 初期値を設定しない場合はnullptrを指定できます。
/// 
/// @return DirectXのリターンコード(HRESULT型)を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT
  DynamicEX::Create
    ( _in<size_t> in_element_size
    , _in<size_t> in_element_num
    , pin<void  > in_ptr_init_data
    )
{
  HRESULT hr = Dynamic::Create( in_element_size, in_element_num, in_ptr_init_data );
  CPL_DBG_OUTPUT_RETURN_VALUE_IF
    ( FAILED(hr)
    , hr
    , SwS("CorePLi: CPL::DX::DynamicEX::Create: バッファの作成に失敗しました。\n")
    );

  try {
    //
    size_t size = GetSize();
    // ユーザーメモリの作成
    m_temp_buffer.Resize( size );
    if( in_ptr_init_data )
    {
      // データコピー
      memcpy( &m_temp_buffer[0], in_ptr_init_data, size );
    }
  }
  catch( ... )
  {
    return E_FAIL;
  }

  return S_OK;
}


// _____________________________________________________________________________
/// @brief マッピング（読み込み専用）
/// 
/// @param[out] out_pp_buf     : 読み込み先のアドレスを格納する変数のアドレスを指定します。
/// @param[in]  in_subresource : この関数では使用しません。
/// 
/// @return 必ずS_OKが返ります。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT DynamicEX::Map( pout<void_cptr> out_pp_buf, _in<UINT> in_subresource ) const
{
  *out_pp_buf = m_temp_buffer.GetTop();
  return S_OK;
}


// _____________________________________________________________________________
/// @brief マッピング（読み書き両用）
/// 
/// @param[in] io_pp_buf      : 入出力先のバッファアドレスを指定します。
/// @param[in] in_subresource : この関数では使用しません。
/// 
/// @return 必ずS_OKが返ります。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT DynamicEX::Map( pout<void_ptr> io_pp_buf, _in<UINT> in_subresource )
{
  *io_pp_buf = m_temp_buffer.GetTop();
  return S_OK;
}


// _____________________________________________________________________________
/// @brief マッピング解除
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DynamicEX::Unmap()
{
  // 何もしない
}


// _____________________________________________________________________________
/// @brief データ書き込み
/// 
/// @param[in] in_index    : 設定する要素の開始インデックスを指定します。
/// @param[in] in_ptr_data : 設定するデータが格納されたアドレスを指定します。
/// @param[in] in_num      : 設定する要素の数を指定します。
/// 
/// @retval S_OK : データ書き込み成功。
/// @retval E_FAIL
///   : エラー。\n
///   : 引数に与えられたパラメタが不正である可能性があります。\n
///   : 書き込みサイズがバッファサイズを上回っていた場合もエラーになります。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT
  DynamicEX::SetData
    ( _in<size_t> in_index
    , pin<void  > in_ptr_data
    , _in<size_t> in_num
    )
{
  if( m_element_size == 0 )
  {
    return E_FAIL;
  }
  else
  {
    if( in_num == 0 )
    {
      return E_FAIL;
    }
    size_t       write_size = m_element_size * in_num;
    const size_t buf_size   = m_temp_buffer.GetSize();
    const size_t buf_offset = in_index * m_element_size;
    if( buf_offset >= buf_size )
    {
      return E_FAIL;
    }
    if( buf_offset + write_size > buf_size )
    {
      write_size = buf_size - buf_offset;
    }
    char_ptr buf_addr_dest = &m_temp_buffer[ buf_offset ];
    ::memcpy( buf_addr_dest, in_ptr_data, write_size );

    m_flag_change = true;

    return S_OK;
  }
}


// _____________________________________________________________________________
/// @brief データ読み込み
/// 
/// @param[in]  in_index       : 取得する要素の最初のインデックスを指定します。
/// @param[out] out_ptr_data   : データを格納するバッファのアドレスを指定します。
/// @param[in]  in_element_num : 取得する要素の数を指定します。
/// 
/// @retval S_OK : データ読み込み成功。
/// @retval E_FAIL
///   : エラー。引数に与えられたパラメタが不正である可能性があります。\n
///   : 書き込みサイズがバッファサイズを上回っていた場合もエラーになります。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT
  DynamicEX::GetData
    ( _in <size_t> in_index
    , pout<void  > out_ptr_data
    , _in <size_t> in_element_num
    ) const
{
  if( m_element_size == 0 )
  {
    return E_FAIL;
  }
  else
  {
    if( in_element_num == 0 )
    {
      return E_FAIL;
    }
    size_t       read_size  = m_element_size * in_element_num;
    const size_t buf_offset = in_index * m_element_size;
    char_cptr buf_addr_src  = &m_temp_buffer[ buf_offset ];
    // コピー
    ::memcpy( out_ptr_data, buf_addr_src, read_size );

    return S_OK;
  }
}


// _____________________________________________________________________________
/// @brief バッファ予約
/// 
/// @param[in] in_element_size : １要素のサイズを指定します。
/// @param[in] in_element_num  : 要素数を指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DynamicEX::Reserve( _in<size_t> in_element_size, _in<size_t> in_element_num )
{
  m_element_size = in_element_size;
  m_temp_buffer.Reserve( in_element_size * in_element_num );
}


// _____________________________________________________________________________
/// @brief バッファサイズの変更
/// 
/// @param[in] in_element_num : 新しい要素数を指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DynamicEX::Resize( _in<size_t> in_element_num )
{
  if( m_element_size == 0 )
  {
    return;
  }
  else
  {
    m_flag_resize = true;
    m_temp_buffer.Resize( m_element_size * in_element_num );
    m_element_num = in_element_num;
  }

  return;
}


// _____________________________________________________________________________
/// @brief データ追加
/// 
/// @param[in] in_index            : 追加する要素の先頭インデックスを指定します。
/// @param[in] in_ptr_ins_data     : 追加するデータを指すアドレスを指定します。
/// @param[in] in_insert_byte_size : 追加するデータのサイズをバイト単位で指定します。
/// 
/// @retval true  : 追加成功。
/// @retval false : 追加失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  DynamicEX::Add
    ( _in<size_t> in_index
    , pin<void  > in_ptr_ins_data
    , _in<size_t> in_insert_byte_size
    )
{
  auto insert_byte_size = in_insert_byte_size;
  if( m_element_size == 0 )
  {
    return false;
  }
  else
  {
    m_flag_change = true;
    m_flag_resize = true;

    if( insert_byte_size == 0 )
    {
      insert_byte_size = this->m_element_size;
    }

    char_cptr ret =
      m_temp_buffer.Add
        ( m_element_size * in_index
        , static_cast<char_cptr>(in_ptr_ins_data)
        , insert_byte_size
        );
    if( ret )
    {
      ++m_element_num;
    }

    return ret != nullptr;
  }
}


// _____________________________________________________________________________
/// @brief 最後尾にデータ追加
/// 
/// @param[in] in_ptr_ins_data
///   : 追加するデータを指すアドレスを指定します。
/// @param[in] in_insert_byte_size
///   : 追加するデータのサイズをバイト単位で指定します。
/// 
/// @retval true  : 追加成功。
/// @retval false : 追加失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  DynamicEX::AddEnd
    ( pin<void  > in_ptr_ins_data
    , _in<size_t> in_insert_byte_size
    )
{
  auto insert_byte_size = in_insert_byte_size;
  if( m_element_size == 0 )
  {
    return false;
  }
  else
  {
    m_flag_change = true;
    m_flag_resize = true;

    if( insert_byte_size == 0 )
    {
      insert_byte_size = this->m_element_size;
    }

    char_cptr ret =
      m_temp_buffer.AddEnd
        ( static_cast<char_cptr>(in_ptr_ins_data)
        , insert_byte_size
        );
    if( ret )
    {
      ++m_element_num;
    }

    return ret != nullptr;
  }
}


// _____________________________________________________________________________
/// @brief データ削除
/// 
/// @param[in] in_index      : 削除する要素の開始インデックスを指定します。
/// @param[in] in_delete_num : 削除する要素の数を指定します。
/// 
/// @retval true  : 削除成功。
/// @retval false : 削除失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool DynamicEX::Delete( _in<size_t> in_index, _in<size_t> in_delete_num )
{
  if( m_element_size == 0 )
  {
    return false;
  }
  else
  {
    m_flag_change = true;

    bool ret =
      m_temp_buffer.Delete
        ( m_element_size * in_index
        , m_element_size * in_delete_num
        );
    if( ret )
    {
      m_element_num = m_temp_buffer.GetSize() / m_element_size;
    }
    return ret;
  }
}


// _____________________________________________________________________________
/// @brief データ削除（インデックステーブルver）
/// 
/// @param[in] in_index_table
///   : 削除するインデックスが格納された配列のポインタを指定します。
/// @param[in] in_table_num
///   : in_index_tableで指定する要素の数を指定します。
///
/// @retval true  : 削除成功。
/// @retval false : 削除失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  DynamicEX::DeleteTable
    ( pin<size_t> in_index_table
    , _in<size_t> in_table_num
    )
{
  if( m_element_size == 0 )
  {
    return false;
  }
  else
  {
    m_flag_change = true;

    // インデックステーブルをバイトオフセットに変換
    Core::Array::Constant<size_t> temp_index_table( in_table_num );
    for( auto x = temp_index_table.begin(); x != temp_index_table.end(); ++x )
    {
      *x *= m_element_size;
    }

    bool ret =
      m_temp_buffer.DeleteTable
        ( &temp_index_table[0]
        , in_table_num
        );
    if( ret )
    {
      m_element_num = m_temp_buffer.GetSize() / m_element_size;
    }
    return ret;
  }
}


// _____________________________________________________________________________
/// @brief 描画前の設定コールバック
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT DynamicEX::RenderSetup( DXDeviceContext* in_ptr_d3d_IC )
{
  if( m_flag_resize ) // リサイズされていれば、バッファを作り直す
  {
    HRESULT hr =
      Create( GetElementSize(), GetElementNum(), &m_temp_buffer[0] );
    CPL_DBG_OUTPUT_RETURN_VALUE_IF
      ( FAILED(hr)
      , hr
      , SwS("CorePLi: CPL::DX::Buffer::DynamicEX::RenderSetup: バッファの作成に失敗\n")
      );

    m_flag_resize = false;
    m_flag_change = false;
  }
  else
  if( m_flag_change )
  {
    AutoMapWD<char> buf( m_cp_dx_buffer );
    memcpy( &buf[0], &m_temp_buffer[0], m_temp_buffer.GetSize() );

    m_flag_change = false;
  }

  return Dynamic::RenderSetup( in_ptr_d3d_IC );
}


// _____________________________________________________________________________
/// @brief バッファ開放
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DynamicEX::Release()
{
  m_cp_dx_buffer.Release();
  m_temp_buffer.Release();
  m_flag_change = false;
  m_flag_resize = false;
}



} // namespace Buffer
//} // namespace Graph
} // namespace DX
} // namespace CPL