/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/11/30
/// 
/// @brief  基底となるとこらへん。
/// 
/// ****************************************************************************


#include "CPL_DX_Kernel.h"


namespace CPL {
namespace DX {


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// デバイス作成パラメータ コンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Graphics_i_::CreateDeviceInfo::CreateDeviceInfo()
  : in_ptr_dx_window( nullptr )
  , in_flag_win_mode( true )
  , in_vs_version( 4.0f )
  , in_ps_version( 4.0f )
  , in_hdl_software_rasterrizer( nullptr )
  , in_create_device_flags( 0 )
{
  //// デバイスタイプ
  //dev_type[0] = D3DDEVTYPE_HAL;
  //dev_type[1] = D3DDEVTYPE_REF;

  //// ピクセルフォーマット
  //pixel_format[0] = D3DFMT_A8R8G8B8;
  //pixel_format[1] = D3DFMT_X8R8G8B8;
  //pixel_format[2] = D3DFMT_R5G6B5;
  //pixel_format[3] = D3DFMT_A1R5G5B5;
  //pixel_format[4] = D3DFMT_X1R5G5B5;
  //pixel_format[5] = D3DFMT_A2R10G10B10;

  //// バックバッファフォーマットの優先度を設定（0が最優先）
  //back_buffer_format[0] = D3DFMT_A8R8G8B8,
  //back_buffer_format[1] = D3DFMT_X8R8G8B8,
  //back_buffer_format[2] = D3DFMT_A2R10G10B10,
  //back_buffer_format[3] = D3DFMT_R5G6B5,
  //back_buffer_format[4] = D3DFMT_X1R5G5B5,
  //back_buffer_format[5] = D3DFMT_A1R5G5B5,
  //back_buffer_format[6] = D3DFMT_A4R4G4B4,
  //back_buffer_format[7] = D3DFMT_A8R3G3B2,
  //back_buffer_format[8] = D3DFMT_R3G3B2,

  //// 深度ステンシルバッファの優先度を設定（0が最優先）
  //depth_stencil_format[0] = D3DFMT_D24S8;
  //depth_stencil_format[1] = D3DFMT_D15S1;
  //depth_stencil_format[2] = D3DFMT_D32;
  //depth_stencil_format[3] = D3DFMT_D16;
  //depth_stencil_format[4] = D3DFMT_D24X4S4;
  //depth_stencil_format[5] = D3DFMT_D24X8;
  //depth_stencil_format[6] = D3DFMT_D24FS8;
}

// _____________________________________________________________________________
/// @brief DirectX用HRESULTに対応するエラーメッセージ取得関数
/// 対応していない値の場合nullptrを返す。
/// 
/// @param[in] hr
///   : DirectXに関する戻り値等に道いられるHRESULT型オブジェクトを渡します。
/// 
/// @return エラーメッセージへのポインタを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
char_cptr GetErrorMessage( HRESULT hr )
{
  switch( hr )
  {
  case DXGI_ERROR_DEVICE_HUNG:
        return "DXGI_ERROR_DEVICE_HUNG:アプリケーションから送信された正しくない形式のコマンドが原因で、アプリケーションのデバイスは失敗しました。";
  case DXGI_ERROR_DEVICE_REMOVED:
        return "DXGI_ERROR_DEVICE_REMOVED:ビデオ カードがシステムから物理的に取り外されているか、ビデオ カードのドライバー アップグレードが行われています。";
  case DXGI_ERROR_DEVICE_RESET:
        return "DXGI_ERROR_DEVICE_RESET:正しくない形式のコマンドが原因で、デバイスは失敗しました。これは実行時の問題です。アプリケーションはデバイスを破棄し、再作成する必要があります。";
  case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
        return "DXGI_ERROR_DRIVER_INTERNAL_ERROR:ドライバーで問題が発生し、デバイスが取り外された状態になっています。";
  case DXGI_ERROR_FRAME_STATISTICS_DISJOINT:
        return "DXGI_ERROR_FRAME_STATISTICS_DISJOINT:要求された機能は、デバイスまたはドライバーではサポートされていません。";
  case DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE:
        return "DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE:要求された機能は、デバイスまたはドライバーではサポートされていません。";
  case DXGI_ERROR_INVALID_CALL:
        return "DXGI_ERROR_INVALID_CALL:アプリケーションが無効なパラメーター データを提供しました。アプリケーションを解放する前に、この問題をデバッグおよび修正する必要があります。";
  case DXGI_ERROR_MORE_DATA:
        return "DXGI_ERROR_MORE_DATA:アプリケーションによって指定されたバッファーは、要求されたデータを格納するのに十分な容量がありません。";
  case DXGI_ERROR_NONEXCLUSIVE:
        return "DXGI_ERROR_NONEXCLUSIVE:アプリケーションが出力の排他的所有権を取得しようとしましたが、他のアプリケーション (またはアプリケーション内のデバイス) が既に所有権を取得しているために失敗しました。";
  case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:
        return "DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:要求された機能は、デバイスまたはドライバーではサポートされていません。";
  case DXGI_ERROR_NOT_FOUND:
        return "DXGI_ERROR_NOT_FOUND:DXGIObject::GetPrivateData の呼び出し時に渡された GUID は、前に IDXGIObject::SetPrivateData または IDXGIObject::SetPrivateDataInterface に渡されたものとして認識できません。IDXGIFactory::EnumAdapters または IDXGIAdapter::EnumOutputs の呼び出し時に列挙された序数が範囲外です。";
  case DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED:
        return "DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED:セッションの切断またはネットワークの切断により、アプリケーションのリモート デバイスが削除されています。アプリケーションは、リモート デバイスがいつ再び利用できるようになるのかを IDXGIFactory1::IsCurrent を呼び出して確認する必要があります。";
  case DXGI_ERROR_REMOTE_OUTOFMEMORY:
        return "DXGI_ERROR_REMOTE_OUTOFMEMORY:メモリー不足またはマシンのエラーにより、アプリケーションのリモート デバイスが失敗しています。アプリケーションはデバイスを破棄し、メモリーの使用量を節約してリソースを再作成する必要があります。";
  case DXGI_ERROR_WAS_STILL_DRAWING:
        return "DXGI_ERROR_WAS_STILL_DRAWING:デバイスがビジー状態で、要求されたタスクがスケジュールされませんでした。このエラーは、Direct3D 10 の非同期クエリのみに適用されます (「ID3D10Asynchronous Interface」を参照してください)。";
  case DXGI_ERROR_UNSUPPORTED:
        return "DXGI_ERROR_UNSUPPORTED:要求された機能は、デバイスまたはドライバーではサポートされていません。";
  default:
    return nullptr;
  }
}


// _____________________________________________________________________________
/// @brief エラーメッセージ出力
/// 
/// @param[in] hr
///   : DirectXに関する戻り値等に道いられるHRESULT型オブジェクトを渡します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void OutputErrorMessage( HRESULT hr )
{
  char_cptr err_msg = GetErrorMessage( hr );
  if( err_msg )
  {
    CPL_OUTPUT_A( "  - %s\n", err_msg );
  }
}



//namespace Graph {

// _____________________________________________________________________________
/// @brief Graphics_i_ コンストラクタ
/// 
/// ※ このコンストラクタを呼ぶ前にDirect3Dオブジェクト設定用コンストラクタを
/// 呼んでおかなければいけない。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Graphics_i_::Graphics_i_()
{
#ifdef CPL_USE_DX9
  if( !sm_CP().m_pD3D )
  {
    throw 0;
  }
#endif
}


// _____________________________________________________________________________
/// @brief Graphics_i_ コンストラクタ
/// 
/// @param[in] in_ptr_d3d_dev : デバイスのポインタを指定します。
/// @param[in] in_ptr_d3d_IC : デバイスコンテキストのポインタを指定します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Graphics_i_::Graphics_i_
( DXDevice*        in_ptr_d3d_dev
, DXDeviceContext* in_ptr_d3d_IC
)
{
#ifdef CPL_USE_DX11
    SetDevice( in_ptr_d3d_dev, in_ptr_d3d_IC );
#elif defined CPL_USE_DX10
    SetDevice( in_ptr_d3d_dev, in_ptr_d3d_IC );
#elif defined CPL_USE_DX9
  if( !in_param.m_pD3D )
  {
    sm_CP().m_pD3D = Direct3DCreate9( D3D_SDK_VERSION );
  }
  else
  {
    sm_CP().m_pD3D = in_param.m_pD3D;
    sm_CP().m_pD3D->AddRef();
  }

  if( in_param.m_pD3DDev )
  {
    sm_CP().m_pD3DDev = in_param.m_pD3DDev;
    sm_CP().m_pD3DDev->AddRef();
  }
#endif
}


// _____________________________________________________________________________
/// @brief デバイス設定
/// 
/// CorePLiのDirectXモジュールにデバイスを関連付けます。
/// 
/// @param[in] in_ptr_d3d_dev : デバイスのポインタを指定します。
/// @param[in] in_ptr_d3d_IC : デバイスコンテキストのポインタを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  Graphics_i_::SetDevice
    ( DXDevice*        in_ptr_d3d_dev
    , DXDeviceContext* in_ptr_d3d_IC
    )
{
  if( !in_ptr_d3d_dev  ||  !in_ptr_d3d_IC )
  {
    return;
  }

  sm_sp_d3d_dev() = in_ptr_d3d_dev;
  sm_sp_d3d_IC()  = in_ptr_d3d_IC;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 解放処理
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//void Graphics_i_::StaticRelease()
//{
//  if( --sm_ref_cnt() == 0 )
//  {
//    sm_CP().Release();
//  }
//}

//} // namespace Graph

} // namespace DX


// _____________________________________________________________________________
/// @brief DX::ColorTypeからタグ形式へ変換
/// 
/// @param[in] in_color : DX::ColorTypeオブジェクトを渡します。
/// 
/// @return タグオブジェクトを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TBTag ToTag( _in<DX::ColorType> in_color )
{
  return TBTag{ "Color", in_color.r, in_color.g, in_color.b, in_color.a };
}


// _____________________________________________________________________________
/// @brief カラータグ書き込み
/// 
/// @param[in] io_tag
///   : 書き込み用のタグオブジェクトを指定します。
/// @param[in] in_color
///   : カラー値を格納したDX::ColorTypeオブジェクトを指定します。
/// 
/// @return タグオブジェクトへの参照を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TBTag& operator << ( rio<TBTag> io_tag, _in<DX::ColorType> in_color )
{
  io_tag = ToTag( in_color );
  return io_tag;
}


// _____________________________________________________________________________
/// @brief カラータグ読み込み
/// 
/// @param[out] out_color
///   : カラー取得用のオブジェクトを指定します。
/// @param[in] in_tag
///   : カラー情報を格納したタグを指定します。
/// 
/// @return カラーオブジェクトへの参照を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DX::ColorType& operator << ( rout<DX::ColorType> out_color, _in<TBTag> in_tag )
{
  auto sp_item = in_tag.GetTagChild();
  if( sp_item ) out_color.r = std::stof(sp_item->GetName()), sp_item = sp_item->GetTagNext();
  if( sp_item ) out_color.g = std::stof(sp_item->GetName()), sp_item = sp_item->GetTagNext();
  if( sp_item ) out_color.b = std::stof(sp_item->GetName()), sp_item = sp_item->GetTagNext();
  if( sp_item ) out_color.a = std::stof(sp_item->GetName());
  return out_color;
}

} // namespace CPL
