/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2015/08/08 - 2017/02/08
/// 
/// @brief  描画テクスチャ関連
/// 
/// ****************************************************************************

#include "CPL_DX_Exception.h"
#include "CPL_DX_RenderTexture.h"
#include "CPL_DX_Graphics.h"

namespace CPL {
namespace DX {


// _____________________________________________________________________________
/// @brief コンストラクタ
/// 
/// レンダリングテクスチャの構築
/// 
/// @param[in] in_width : テクスチャの横幅を指定します。
/// @param[in] in_height : テクスチャの縦幅を指定します。
/// @param[in] in_format_color : カラーフォーマットを指定します。
/// @param[in] in_format_depth : 深度ステンシルフォーマットを指定します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RenderTexture::RenderTexture
( _in<size_t       > in_width
, _in<size_t       > in_height
, _in<DXGI_FORMAT  > in_format_color
, _in<DXGI_FORMAT  > in_format_depth
)
{
  auto flag_create =
    Create
      ( in_width
      , in_height
      , in_format_color
      , in_format_depth
      );
  CPL_THROW_IF
    ( !flag_create
    , "Failed to Create RenderTexture.\n"
    );
}


// _____________________________________________________________________________
/// @brief コピーコンストラクタ
/// 
/// 共有ではなく複製。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RenderTexture::RenderTexture( cref<RenderTexture> in_render_texture )
{
  auto& rt = in_render_texture;
  // テクスチャ情報取得
  DX_TEXTURE2D_DESC desc_img, desc_depth;
  rt.m_cp_tex_color->GetDesc( &desc_img );
  desc_depth.Format = DXGI_FORMAT_UNKNOWN;
  if( in_render_texture.m_cp_tex_depth_stencil )
  {
    in_render_texture.m_cp_tex_depth_stencil->GetDesc( &desc_depth );
  }
  // 作成
  auto res_create =
    this->Create
      ( desc_img.Width
      , desc_img.Height
      , desc_img.Format
      , desc_depth.Format
      );
  if( res_create )
  {
    // ***** イメージコピー *****
    // コピー元リソース
    CpDXResource cp_imgRes_of_source, cp_imgRes_of_dest;
    in_render_texture.m_cp_view_render_target->GetResource( &cp_imgRes_of_source );
    // コピー先リソース
    m_cp_view_render_target->GetResource( &cp_imgRes_of_dest );
    //
    Graphics_i_::GetImmediateContext()->CopyResource
      ( cp_imgRes_of_dest
      , cp_imgRes_of_source
      );

    // 深度バッファ
    if( in_render_texture.m_cp_tex_depth_stencil )
    {
      CpDXResource cp_depthRes_of_source, cp_depthRes_of_dest;
      in_render_texture.m_cp_view_depth_stencil->GetResource( &cp_depthRes_of_source );
      this->m_cp_view_depth_stencil->GetResource( &cp_depthRes_of_dest );
      //
      Graphics_i_::GetImmediateContext()->CopyResource
        ( cp_depthRes_of_dest
        , cp_depthRes_of_source
        );
    }
  }
}


// _____________________________________________________________________________
/// @brief レンダーテクスチャの作成
/// 
/// @param[in] in_width : テクスチャの横幅を指定します。
/// @param[in] in_height : テクスチャの縦幅を指定します。
/// @param[in] in_format_color : カラーフォーマットを指定します。
/// @param[in] in_format_depth : 深度ステンシルフォーマットを指定します。
/// 
/// @retval ture  : 成功
/// @retval false : 失敗
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  RenderTexture::Create
    ( _in<size_t     > in_width
    , _in<size_t     > in_height
    , _in<DXGI_FORMAT> in_format_color
    , _in<DXGI_FORMAT> in_format_depth
    )
{
  auto& ptr_d3ddev = DX::Graphics::GetDevice();

  //UINT tex_width  = AdjustPowerd2(static_cast<UINT>(in_width));
  //UINT tex_height = AdjustPowerd2(static_cast<UINT>(in_height));
  UINT tex_width  = static_cast<UINT>(in_width);
  UINT tex_height = static_cast<UINT>(in_height);

  // バッファとなるテクスチャの作成
  DX::CpDXTexture2D cp_BBT; // バックバッファテクスチャ
  D3D11_TEXTURE2D_DESC desc_BBT;
  ZeroMemory( &desc_BBT, sizeof(desc_BBT) );
  desc_BBT.Width              = tex_width;
  desc_BBT.Height             = tex_height;
  desc_BBT.MipLevels          = 1;
  desc_BBT.ArraySize          = 1;
  desc_BBT.Format             = in_format_color;
  desc_BBT.SampleDesc.Count   = 1;
  desc_BBT.SampleDesc.Quality = 0;
  desc_BBT.Usage              = D3D11_USAGE_DEFAULT;
  desc_BBT.BindFlags          = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
  desc_BBT.CPUAccessFlags     = 0;
  desc_BBT.MiscFlags          = 0;
  HRESULT hr =
    ptr_d3ddev->CreateTexture2D
      ( &desc_BBT
      , nullptr
      , &cp_BBT
      );
  CPL_THROW_FAILED
    ( hr
    , "Failed to create a BackBufferTexture.\n"
      "CorePLi :  - CPL::GUC::RenderTexture::Create()\n"
    );

  // テクスチャからシェーダーリソースビューを作成
  DX::CpDXShaderResourceView cp_SRV;
  ptr_d3ddev->CreateShaderResourceView( cp_BBT, nullptr, &cp_SRV );

  // テクスチャからレンダリングターゲットビューを作成
  DX::CpDXRenderTargetView cp_RTV;
  hr =
    ptr_d3ddev->CreateRenderTargetView
      ( cp_BBT
      , nullptr
      , &cp_RTV
      );
  CPL_THROW_FAILED
    ( hr
    , "Failed to create a RenderTargetView.\n"
      "CorePLi :  - CPL::DX::RenderRexture::Create()\n"
    );

  // 深度ステンシルバッファ（テクスチャ）を作成
  DX::CpDXTexture2D cp_DSB;
  DX::CpDXDepthStencilView cp_DSV;
  if( in_format_depth )
  {
    D3D11_TEXTURE2D_DESC desc_DSB;
    ZeroMemory( &desc_DSB, sizeof(desc_DSB) );
    desc_DSB.Width              = tex_width;
    desc_DSB.Height             = tex_height;
    desc_DSB.MipLevels          = 1;
    desc_DSB.ArraySize          = 1;
    desc_DSB.Format             = in_format_depth;
    desc_DSB.SampleDesc.Count   = 1;
    desc_DSB.SampleDesc.Quality = 0;
    desc_DSB.Usage              = D3D11_USAGE_DEFAULT;
    desc_DSB.BindFlags          = D3D11_BIND_DEPTH_STENCIL;
    desc_DSB.CPUAccessFlags     = 0;
    desc_DSB.MiscFlags          = 0;
    hr =
      ptr_d3ddev->CreateTexture2D
        ( &desc_DSB
        , nullptr
        , &cp_DSB
        );
    CPL_THROW_FAILED
      ( hr
      , "Failed to create a DepthStencilBuffer.\n"
        "CorePLi :  - CPL::GUC::RenderTexture::Create()\n"
      );

    // 深度ステンシルビューの作成
    //D3D11_DEPTH_STENCIL_VIEW_DESC desc_DSV;
    //ZeroMemory( &desc_DSV, sizeof(desc_DSV) );
    //desc_DSV.Format             = desc_DSB.Format;
    //desc_DSV.ViewDimension      = D3D11_DSV_DIMENSION_TEXTURE2D;
    //desc_DSV.Texture2D.MipSlice = 0;
    hr =
      ptr_d3ddev->CreateDepthStencilView
        ( cp_DSB
        , nullptr
        , &cp_DSV
        );
    CPL_THROW_FAILED
      ( hr
      , "Failed to create a DepthStencilView.\n"
        "CorePLi :  - CPL::GUC::RenderTexture::Create()\n"
      );
  }

  // 保持するポインタに設定
  m_cp_tex_color            = cp_BBT;
  m_cp_view_shader_resource = cp_SRV;
  m_cp_view_render_target   = cp_RTV;
  m_cp_tex_depth_stencil    = cp_DSB;
  m_cp_view_depth_stencil   = cp_DSV;

  return true;
}


// _____________________________________________________________________________
/// @brief テクスチャのリサイズ
/// 
/// @param[in] in_width : テクスチャの横幅を指定します。
/// @param[in] in_height : テクスチャの縦幅を指定します。
/// 
/// @retval true  : テクスチャのリサイズに成功。
/// @retval false : テクスチャのリサイズに失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool RenderTexture::Resize( _in<size_t> in_width, _in<size_t> in_height )
{
  DX_TEXTURE2D_DESC desc_tex2d, desc_DS;
  m_cp_tex_color        ->GetDesc( &desc_tex2d );
  m_cp_tex_depth_stencil->GetDesc( &desc_DS );

  return
    Create
      ( in_width
      , in_height
      , desc_tex2d.Format
      , desc_DS.Format
      );
}

// _____________________________________________________________________________
/// @brief レンダリングターゲットの切り替え
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool RenderTexture::ChangeTarget()
{
  auto& cp_d3d_IC = DX::Graphics_i_::GetImmediateContext();

  // ターゲット設定
  cp_d3d_IC->OMSetRenderTargets
    ( 1
    , &m_cp_view_render_target
    , m_cp_view_depth_stencil
    );

  // 
  return true;
}

// _____________________________________________________________________________
/// @brief カラーバッファのクリア
/// 
/// @param[in] in_rgba
///   : RGBAカラーをfloat4型で指定します。\n
///   : 各要素はfloat値、0.0～1.0になります。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void RenderTexture::ClearColor( _in<float4> in_rgba )
{
  auto& cp_d3d_IC = DX::Graphics_i_::GetImmediateContext();

  cp_d3d_IC->ClearRenderTargetView
    ( m_cp_view_render_target
    , in_rgba.p
    );
}

// _____________________________________________________________________________
/// @brief 深度ステンシルバッファのクリア
/// 
/// @param[in] in_depth
///   : 設定する深度を0.0～1.0の範囲で指定します。\n
///   : この値で深度バッファが初期化されます。
/// @param[in] in_stencil
///   : ステンシル値を0～255の範囲で指定します。\n
///   : この値でステンシルバッファが初期化されます。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void RenderTexture::ClearDepthStencil( _in<float> in_depth, _in<U8> in_stencil )
{
  auto& cp_d3d_IC = DX::Graphics_i_::GetImmediateContext();

  cp_d3d_IC->ClearDepthStencilView
    ( m_cp_view_depth_stencil
    , D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL
    , in_depth
    , in_stencil
    );
}

// _____________________________________________________________________________
/// @brief 深度バッファのクリア
/// 
/// @param[in] in_depth
///   : 設定する深度を0.0～1.0の範囲で指定します。\n
///   : この値で深度バッファが初期化されます。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void RenderTexture::ClearDepth( _in<float> in_depth )
{
  auto& cp_d3d_IC = DX::Graphics_i_::GetImmediateContext();

  cp_d3d_IC->ClearDepthStencilView
    ( m_cp_view_depth_stencil
    , D3D11_CLEAR_DEPTH
    , in_depth
    , 0
    );
}

// _____________________________________________________________________________
/// @brief ステンシルバッファのクリア
/// 
/// @param[in] in_stencil
///   : ステンシル値を0～255の範囲で指定します。\n
///   : この値でステンシルバッファが初期化されます。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void RenderTexture::ClearStencil( _in<U8> in_stencil )
{
  auto& cp_d3d_IC = DX::Graphics_i_::GetImmediateContext();

  cp_d3d_IC->ClearDepthStencilView
    ( m_cp_view_depth_stencil
    , D3D11_CLEAR_STENCIL
    , 0.0f
    , in_stencil
    );
}

} // namespace DX
} // namespace CPL
