/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2014/02/02
/// 
/// @brief  DirectGraphics全般。
/// 
/// ****************************************************************************


#include "CPL_DX_Graphics.h"
#include "CPL_DX_Frame.h"
#include "CPL_DX_Factory.h"



namespace CPL {
namespace DX {
//namespace Graph {

  //int g_autolock_indent = 0;


// _____________________________________________________________________________
/// @brief コンストラクタ
/// 
/// @param[in] in_ptr_d3d_dev
///   : Direct3Dデバイスポインタを指定します。\n
///   : ユーザーが既に作成していたら設定することが可能です。\n
///   : Graphics::Create()で作成する場合は、nullptrでかまいません。
/// @param[in] in_ptr_d3d_IC
///   : Direct3Dのイミディエイトとコンテキストポインタを指定します。\n
///   : ユーザーが既に作成していたら設定することが可能です。\n
///   : Graphics::Create()で作成する場合は、nullptrでかまいません。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Graphics::Graphics
( DXDevice*        in_ptr_d3d_dev
, DXDeviceContext* in_ptr_d3d_IC
) : Graphics_i_( in_ptr_d3d_dev, in_ptr_d3d_IC )
{
//  m_pDefCamera = nullptr;

  //::memset( &m_d3d_pp, 0, sizeof(m_d3d_pp) );
  //// デフォルト設定
  //m_d3d_pp.EnableAutoDepthStencil = TRUE;
  //m_d3d_pp.SwapEffect             = D3DSWAPEFFECT_DISCARD; // 
  //m_d3d_pp.Windowed               = true; // ウィンドウモード
  //m_d3d_pp.BackBufferCount        = 1;
  //m_d3d_pp.BackBufferWidth        = 640;
  //m_d3d_pp.BackBufferHeight       = 480;
  //m_d3d_pp.BackBufferFormat       = D3DFMT_UNKNOWN;
  //m_d3d_pp.AutoDepthStencilFormat = D3DFMT_D24S8;
}


// _____________________________________________________________________________
/// @brief デストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Graphics::~Graphics()
{
  m_window_table.Release();
/*  // 登録されたウィンドウから、Graphics側で生成したウィンドウを破棄する
  long num = m_window_table.GetNum();
  while( --num >= 0 )
  {
    if( !m_window_table[ num ]->GetFlag_hWndInstance_() )
    {
//      m_window_table[ num ]->Destroy();
    }
  }*/
}


// _____________________________________________________________________________
/// @brief デバイス作成
/// 
/// @param[in] io_create_info
///   : デバイス作成情報を指定します。\n
///   : 詳しくはCreateDeviceInfoを参照してください。
/// 
/// @retval true  : 作成成功。
/// @retval false : 作成失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Graphics::Create( _in<CreateDeviceInfo> io_create_info )
{
  // ウィンドウが設定されていなければ、抜ける。
  if( !io_create_info.in_ptr_dx_window )
  {
    return false;
  }

  D3D_DRIVER_TYPE driver_types[] =
  { D3D_DRIVER_TYPE_HARDWARE
  , D3D_DRIVER_TYPE_WARP
  , D3D_DRIVER_TYPE_REFERENCE
  };
  UINT driver_types_num = CPL_ARRAY_NUM( driver_types );
  
  D3D_FEATURE_LEVEL feature_levels[] =
  { D3D_FEATURE_LEVEL_11_0
  , D3D_FEATURE_LEVEL_10_1
  , D3D_FEATURE_LEVEL_10_0
  };
  UINT feature_levels_num = CPL_ARRAY_NUM( feature_levels );

  // アダプタテーブル取得
  Graphics::AdapterTable adapters;
  int adapter_num = this->EnumAdapters( adapters );
  
  UINT create_device_flags = io_create_info.in_create_device_flags;
#ifdef _DEBUG
  create_device_flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

  // 作成するDirectXインターフェース
  CpDXDevice        cp_d3d_dev;
  CpDXDeviceContext cp_d3d_dev_IC;

  HRESULT hr = S_OK;

  // アダプタ数ループ
  //CPL_LOOP2( adapter_num, adapter_idx )
  {
    // ドライバ数ループ
    //CPL_LOOP2( driver_types_num, driver_types_idx )
    for
      ( size_t driver_types_idx = 0
      ; driver_types_idx < driver_types_num
      ; ++driver_types_idx
      )
    {
      hr =
        D3D11CreateDevice
          ( nullptr/*adapters[ adapter_idx ]*/
          , driver_types[ driver_types_idx ]
          , io_create_info.in_hdl_software_rasterrizer
          , create_device_flags
          , feature_levels
          , feature_levels_num
          , D3D11_SDK_VERSION
          , &cp_d3d_dev
          , &io_create_info.out_feature_levels
          , &cp_d3d_dev_IC
          );

      if( SUCCEEDED( hr ) )
      {
        break;
      }
    } // loop driver_type
  } // loop adapter_num

  if( FAILED( hr ) )
  {
    return false;
  }

  this->sm_sp_d3d_dev() = cp_d3d_dev;
  this->sm_sp_d3d_IC()  = cp_d3d_dev_IC;

  return true;
}


// _____________________________________________________________________________
/// @brief 登録されたウィンドウクラス破棄通知
/// 
/// @param[in] in_ptr_dx_window
///   : 破棄通知を行うDirectX用Windowオブジェクトのポインタを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Graphics::SendDestroyWindow( DX::Window* in_ptr_dx_window )
{
  SDEF num = static_cast<SDEF>(m_window_table.GetNum());
  while( --num >= 0 )
  {
    if( m_window_table[ num ] == in_ptr_dx_window )
    {
      m_window_table.Delete( num );
      return;
    }
  }
}


// _____________________________________________________________________________
/// @brief 終了処理
/// 
/// ※ 今のところ何も処理していないので、呼んでも無意味です。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Graphics::Release()
{
  // 今のところなにもしない
}


// _____________________________________________________________________________
/// @brief アダプタを列挙する。
/// 
/// @param[out] out_adapter_table
///   : アダプタテーブル取得用オブジェクトを指定します。\n
///   : 詳しくはGraphics::AdapterTableを参照してください。
/// 
/// @return 成功するとアダプタ数を返します。\n失敗すると−１を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int Graphics::EnumAdapters( AdapterTable& out_adapter_table ) const
{
  try {
    DX::Factory factory; // ファクトリの作成（失敗で例外を投げる）

    IDXGIAdapter* ptr_adapter = nullptr; // スマートポインタにしてはいけない。
    UINT count = 0;
    while( factory->EnumAdapters( count, &ptr_adapter ) != DXGI_ERROR_NOT_FOUND )
    {
      out_adapter_table.AddEnd( CpGIAdapter(ptr_adapter) );
      ++count;

      RELEASE(ptr_adapter);
    }

    return (int)count;
  }
  catch(...)
  {
    return -1;
  }
}


// _____________________________________________________________________________
/// @brief DirectXウィンドウ登録
/// 
/// @param[in] in_ptr_dx_window : 登録するDirectXウィンドウのポインタを指定します。
/// 
/// @retval S_OK : ウィンドウ登録成功。
/// @retval E_FAIL : in_ptr_dx_windowがウィンドウ無効です。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT Graphics::AddWindow( DX::Window* in_ptr_dx_window )
{
  if( !in_ptr_dx_window )
  {
    return E_FAIL;
  }

  m_window_table.AddEnd( in_ptr_dx_window );

  return S_OK;
}


// _____________________________________________________________________________
/// @brief 登録されているウィンドウを取得
/// 
/// @param[in] in_index : 取得したいウィンドウのインデックスを指定します。
/// 
/// @return DirectXウィンドウのポインタを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cptr<DX::Window> Graphics::GetWindow( _in<int> in_index ) const
{
  return m_window_table[ in_index ];
}


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