/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2009?/X/X - 2014/02/02
/// 
/// @brief  バッファ基本操作周り。
/// 
/// ****************************************************************************

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


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


// _____________________________________________________________________________
/// @brief コンストラクタ。
/// 
/// @param[in] in_slot_No : スロット番号。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BaseBuffer_::BaseBuffer_( _in<int> in_slot_No )
  : m_element_size( 0 )
  , m_element_num( 0 )
  , m_slot_No( in_slot_No )
  , m_buf_name( "" )
{
  memset( M_Pipeline, 0, sizeof(M_Pipeline) );
}


// _____________________________________________________________________________
/// @brief レンダリングのセットアップ。
/// 
/// @param[in] in_ptr_d3d_IC : 関連付けるデバイスコンテキストのポインタ。
/// 
/// @retval S_OK : 成功。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT BaseBuffer_::RenderSetup( DXDeviceContext* in_ptr_d3d_IC )
{
  // バインドタイプを取得
  //UINT bind_flag = this->GetBindFlags();
  // デフォルト値を考慮したスロットNoを取得
  int slot_No = (GetSlotNo() > 0) ? GetSlotNo() : 0;

  Pipeline::Function* pipeline_func = M_Pipeline;
  while( *pipeline_func )
  {
    (*pipeline_func)( in_ptr_d3d_IC, this, slot_No );
    ++pipeline_func;
  }

  return S_OK;
}

// _____________________________________________________________________________
/// @brief パイプライン転送コールバック設定。
/// 
/// @param[in] in_pipeline_function_list
///   : パイプライン関数テーブルの先頭ポインタを指定します。
/// @param[in] in_func_num
///   : パイプライン関数テーブルの要素数を指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  BaseBuffer_::SetPipeline
    ( pin<Pipeline::Function> in_pipeline_function_list
    , _in<int               > in_func_num
    )
{
  // ポインタテーブル設定
  //CPL_LOOP2( Min( in_func_num, PIPELINE_SET_MAX_NUM ), func_idx )
  for
    ( size_t func_idx = 0, num = Min(in_func_num,PIPELINE_SET_MAX_NUM)
    ; func_idx < num
    ; ++func_idx
    )
  {
    M_Pipeline[ func_idx ] = in_pipeline_function_list[ func_idx ];
  }
}


// _____________________________________________________________________________
/// @brief バッファ作成。
/// 
/// @param[in] in_element_size  : １要素のサイズ。
/// @param[in] in_element_num   : 要素数。
/// @param[in] in_ptr_init_data : 初期値を格納した領域へのポインタ。nullptrを指定可。
/// 
/// @return 
/// S_OK : 成功。                                                              \n
/// E_INVALIDARG : 引数が不正です。                                            \n
/// それ以外は、DirectXのCreateBufferを検索してください。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT
  BaseBuffer_::Create
    ( _in<size_t> in_element_size
    , _in<size_t> in_element_num
    , pin<void  > in_ptr_init_data
    )
{
  if( in_element_size == 0  ||  in_element_num == 0 )
  {
    return E_INVALIDARG;
  }

  CPL_DBG_OUTPUT_RETURN_VALUE_IF
    ( !Graphics_i_::GetDevice()
    , E_POINTER
    , SwS("A buffer cannot be created. DX device pointer is invalid.\n")
    );

  m_cp_dx_buffer.Release();

  m_element_size = in_element_size;
  m_element_num  = in_element_num;

  D3D11_USAGE usage_flag = GetUsage();     // 使用法のフラグを取得
  UINT        bind_flags = GetBindFlags(); // バインドフラグ取得

  // バッファ情報を設定
  // ※ 使用法がステージングの場合はバインドフラグは０とする
  DX_BUFFER_DESC buf_desc;
  buf_desc.ByteWidth      = static_cast<UINT>(in_element_size * in_element_num);
  buf_desc.Usage          = usage_flag;
  buf_desc.BindFlags      = usage_flag == D3D11_USAGE_STAGING ? 0 : bind_flags;
  buf_desc.CPUAccessFlags = GetCPUAccessFlags();
  buf_desc.MiscFlags      = GetMiscFlags();
  buf_desc.StructureByteStride = static_cast<UINT>(in_element_size);

  // 初期化情報を設定
  DX_SUBRESOURCE_DATA init_data;
  init_data.pSysMem          = in_ptr_init_data;
  init_data.SysMemPitch      = GetSysMemPichi();
  init_data.SysMemSlicePitch = GetSysMemSlicePitch();

  // バッファの作成
  HRESULT hr =
    Graphics_i_::GetDevice()->CreateBuffer
      ( &buf_desc
      , (buf_desc.Usage==D3D11_USAGE_IMMUTABLE || in_ptr_init_data) ? &init_data : nullptr
      , &m_cp_dx_buffer
      );

  if( FAILED(hr) )
  {
    return hr;
  }
  
    //D3D11_BIND_VERTEX_BUFFER = 0x1L,
    //D3D11_BIND_INDEX_BUFFER = 0x2L,
    //D3D11_BIND_CONSTANT_BUFFER = 0x4L,
    //D3D11_BIND_SHADER_RESOURCE = 0x8L,
    //D3D11_BIND_STREAM_OUTPUT = 0x10L,
    //D3D11_BIND_RENDER_TARGET = 0x20L,
    //D3D11_BIND_DEPTH_STENCIL = 0x40L,
    //D3D11_BIND_UNORDERED_ACCESS = 0x80L,
  // デフォルトのパイプラインを設定
  if( bind_flags & (D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_INDEX_BUFFER) )
  {
    Pipeline::Function pl_tbl[] = { Pipeline::InputAssembler };
    SetPipeline( pl_tbl, 1 );
  }
  else
  if( bind_flags & (D3D11_BIND_CONSTANT_BUFFER | D3D11_BIND_SHADER_RESOURCE) )
  {
    Pipeline::Function pl_tbl[] = { Pipeline::VertexShader };
    SetPipeline( pl_tbl, 1 );
  }

  // シェーダーリソースの場合、シェーダーリソースビューを作成して関連付ける
  if( bind_flags == D3D11_BIND_SHADER_RESOURCE )
  {
    // シェーダーリソースビューの作成
    DXShaderResourceView* ptr_shader_RV = nullptr;
    Graphics::GetDevice()->CreateShaderResourceView
      ( m_cp_dx_buffer
      , nullptr
      , &ptr_shader_RV
      );
    // 関連付け
    m_cp_dx_buffer->SetPrivateDataInterface
      ( __uuidof(DXShaderResourceView)
      , ptr_shader_RV
      );
  }

  return S_OK;
}

// _____________________________________________________________________________
/// @brief バッファ開放。
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void BaseBuffer_::Release()
{
  m_cp_dx_buffer.Release();
}


/// @cond
// _____________________________________________________________________________
/// @brief マッピング（マップモード指定ver：読み書き対応）
/// 
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT
  BaseBuffer_::zzf_Map
    ( D3D11_MAP in_flag_map
    , void_ptr* io_pp_buf
    , UINT      in_subresource
    )
{
  if( !io_pp_buf )
  {
    return E_HANDLE;
  }

  // バッファマッピング
  DX_MAPPED_SUBRESOURCE mapped_source;
  HRESULT hr =
    Graphics_i_::GetImmediateContext()->Map(
      m_cp_dx_buffer,
      in_subresource,
      in_flag_map,
      0,
      &mapped_source );

  *io_pp_buf = mapped_source.pData;

  return hr;
}


// _____________________________________________________________________________
/// @brief マッピング（読み取り専用）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT
  BaseBuffer_::zzf_Map
    ( void_cptr* out_pp_buf
    , UINT       in_subresource
    ) const
{
  if( !out_pp_buf )
  {
    return E_HANDLE;
  }

  // バッファマッピング
  DX_MAPPED_SUBRESOURCE mapped_source;
  HRESULT hr =
    Graphics_i_::GetImmediateContext()->Map(
      m_cp_dx_buffer,
      in_subresource,
      D3D11_MAP_READ,
      0,
      &mapped_source );

  *out_pp_buf = mapped_source.pData;

  return hr;
}
/// @endcond


// _____________________________________________________________________________
/// @brief マッピング解除
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void BaseBuffer_::Unmap()
{
  Graphics_i_::GetImmediateContext()->Unmap( m_cp_dx_buffer, 0 );
}


/// @cond

// _____________________________________________________________________________
/// @brief データ設定。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT
  BaseBuffer_::zzf_SetData
    ( D3D11_MAP in_flag_map
    , size_t    in_index
    , void_cptr in_ptr_data
    , size_t    in_element_num
    )
{
  void_ptr ptr_dest;
  HRESULT hr = zzf_Map( in_flag_map, &ptr_dest );
  if( SUCCEEDED(hr) )
  {
    ::memcpy( ptr_dest, in_ptr_data, in_element_num * GetElementSize() );
  }

  return hr;
}


// _____________________________________________________________________________
/// @brief データ取得。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT
  BaseBuffer_::zzf_GetData
    ( size_t    in_index
    , void_ptr  out_ptr_data
    , size_t    in_element_num
    ) const
{
  void_cptr ptr_src;
  HRESULT hr = zzf_Map( &ptr_src );
  if( SUCCEEDED(hr) )
  {
    ::memcpy( out_ptr_data, ptr_src, in_element_num * GetElementSize() );
  }

  return hr;
}

/// @endcond



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