/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2014/12/31
/// 
/// @brief  DirectX 頂点シェーダー・ピクセルシェーダ共通部分。
/// 
/// ****************************************************************************


#include "CPL_DX_Shader_Base.h"

#include <D3Dcompiler.h>

namespace CPL {
namespace DX {
namespace Shader {


// _____________________________________________________________________________
/// @brief 最終的に呼び出されるシェーダーコンパイル関数
/// 
/// @param[in] in_shader_code
///   : シェーダ―用コードオブジェクトを指定します。
/// @param[in] in_shader_function_name
///   : シェーダ―のメイン関数名を指定します。
/// @param[in] in_target_version
///   : シェーダ―のターゲットバージョンを指定します。
/// @param[in] in_ptr_defines
///   : シェーダ―マクロを指定します。\n
///   : 詳しくはD3D10_SHADER_MACROで検索してください。\n
///   : 省略する場合、nullptrを指定します。
/// @param[in] in_flag_compile
///   : シェーダ―のコンパイルオプションです。\n
///   : 詳細はD3DCompileのFlags1を検索してください。
/// @param[in] in_flag_effect
///   : エフェクトのコンパイルオプションです。\n
///   : 詳細はD3DCompileのFlags2を検索してください。
/// @param[out] out_pp_error
///   : コンパイラのエラーメッセージを格納するID3D10Blobへのポインターを指定します。\n
///   : 省略する場合、nullptrを指定します。
/// 
/// @return DirectXのリターンコード(HRESULT)を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT
  ShaderBase::zzf_CompileFromMemory
    ( _in<ShaderCode        > in_shader_code
    , _in<char_cptr         > in_shader_function_name
    , _in<LPCSTR            > in_target_version
    , pin<D3D10_SHADER_MACRO> in_ptr_defines
    , _in<UINT              > in_flag_compile
    , _in<UINT              > in_flag_effect
    , pout<ID3DBlob*        > out_pp_error
    )
{
  auto flag_compile = in_flag_compile;
#ifdef CPL_DEBUG
  flag_compile |= D3DCOMPILE_DEBUG;
#endif


  DXBlob* ptr_err_buf = nullptr;
#ifdef CPL_USE_DX11ex
  HRESULT hr =
    D3DCompile
      ( in_shader_code.GetCode()       // src data
      , in_shader_code.GetCodeLength() // src data length
      , nullptr                        // filename
      , in_ptr_defines                 // macro defines
      , nullptr                        // include
      , in_shader_function_name        // function name(entry point)
      , in_target_version              // target
      , flag_compile                   // flag1
      , in_flag_effect                 // flag2
      , &m_cp_dx_blob                  // get shader pointer
      , &ptr_err_buf                   // get error message
      );
#else
  HRESULT hr =
    D3DX11CompileFromMemory
      ( in_shader_code.GetCode()        // src data
      , in_shader_code.GetCodeLength()  // src data length
      , nullptr                         // filename
      , in_ptr_defines                  // macro defines
      , nullptr                         // include
      , in_shader_function_name         // function name
      , in_target_version               // profile
      , flag_compile                 // flag1
      , in_flag_effect                  // flag2
      , nullptr                         // pump 
      , &m_cp_dx_blob                   // get shader pointer
      , &ptr_err_buf                    // get error message
      , nullptr                         // get hresult
      );
#endif

  if( out_pp_error )
  {
    *out_pp_error = ptr_err_buf;
  }
#ifdef CPL_DBG_OUTPUT_SHADER
  // デバッグモードならばソースを出力する
  CPL_OUTPUT_A( "CorePLi output Shader Code ------------\n" );
  CPL_OUTPUT_A( in_shader_code.GetCode() );
  CPL_OUTPUT_A( "---------------------------------------\n" );
  
#endif
  if( ptr_err_buf )
  {
    CPL_OUTPUT_A
      ( "CorePLi shader compile error\n%s\n"
      , static_cast<char*>(ptr_err_buf->GetBufferPointer())
      );
  }

  return hr;
}


// _____________________________________________________________________________
/// @brief 最終的に呼び出されるシェーダーコンパイル関数（ファイル読み込みversion）
/// 
/// @param[in] in_file_name
///   : シェーダーが記述されたファイルの名前を指定します。
/// @param[in] in_shader_function_name
///   : シェーダーのメイン関数名を指定します。
/// @param[in] in_target_version
///   : シェーダーのターゲットバージョンを指定します。\n
///   : 例 "ps_4_0"
/// @param[in] in_ptr_defines
///   : シェーダ―マクロを指定します。\n
///   : 詳しくはD3D10_SHADER_MACROで検索してください。\n
///   : 省略する場合、nullptrを指定します。
/// @param[in] in_flag_compile
///   : シェーダ―のコンパイルオプションです。\n
///   : 詳細はD3DCompileのFlags1を検索してください。
/// @param[in] in_flag_effect
///   : エフェクトのコンパイルオプションです。\n
///   : 詳細はD3DCompileのFlags2を検索してください。
/// @param[out] out_pp_error
///   : コンパイラのエラーメッセージを格納するID3D10Blobへのポインターを指定します。\n
///   : 省略する場合、nullptrを指定します。
/// 
/// @return DirectXのリターンコード(HRESULT)を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HRESULT
  ShaderBase::zzf_CompileFromFile
    ( pin<str_type          > in_file_name
    , _in<char_cptr         > in_shader_function_name
    , _in<LPCSTR            > in_target_version
    , pin<D3D10_SHADER_MACRO> in_ptr_defines
    , _in<UINT              > in_flag_compile
    , _in<UINT              > in_flag_effect
    , pout<ID3DBlob*        > out_pp_error
    )
{
  auto flag_compile = in_flag_compile;
#ifdef CPL_DEBUG
  flag_compile |= D3DCOMPILE_DEBUG;
#endif

  DXBlob* ptr_err_buf = nullptr;
#ifdef CPL_USE_DX11ex

  HRESULT hr =
    D3DCompileFromFile
      ( Core::ChangeCodeStrings<Core::StringW>( in_file_name ) // filename
      , in_ptr_defines          // macro defines
      , nullptr                 // include
      , in_shader_function_name // function name(entry point)
      , in_target_version       // target
      , flag_compile         // flag1
      , in_flag_effect          // flag2
      , &m_cp_dx_blob           // get shader pointer
      , &ptr_err_buf            // get error message
      );

#else

  HRESULT hr =
    D3DX11CompileFromFile
      ( in_file_name
      , in_ptr_defines          // macro defines
      , nullptr                 // include
      , in_shader_function_name // function name
      , in_target_version       // profile
      , flag_compile         // flag1
      , in_flag_effect          // flag2
      , nullptr                 // pump 
      , &m_cp_dx_blob           // get shader pointer
      , &ptr_err_buf            // get error message
      , nullptr                 // get hresult
      );

#endif
  if( out_pp_error )
  {
    *out_pp_error = ptr_err_buf;
  }

#ifdef CPL_DEBUG
  if( ptr_err_buf )
  {
    CPL_OUTPUT_A
      ( "shader compile error\n%s\n"
      , static_cast<char*>(ptr_err_buf->GetBufferPointer())
      );
  }
#endif

  return hr;
}



/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 要素テーブルの使用方法の洗い出し？
// 専用のビットフィールドに使用の可否が設定される
// ただし、TexCoordとColorはそれぞれ4bit与えられ、
// TexCoordにはテクスチャステージ数、
// Colorには0bit目にdiffuseの有無、1bit目にspecularの有無がセットされる（今のところvs_2_0仕様）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ShaderBase::_ELEMENT_USAGE ShaderBase::_GetElementUsageFlag( DX_IN_ELEMENT_DESC const* pElement )
{
  const DX_IN_ELEMENT_DESC end = D3DDECL_END();
  _ELEMENT_USAGE flag;
  while( memcmp(pElement, &end, sizeof(end)) )
  {
    switch( pElement->Usage )
    {
    case D3DDECLUSAGE_POSITION:     flag.Position     = true; break;
      case D3DDECLUSAGE_BLENDWEIGHT:  flag.BlendWeight  = true; break;
      case D3DDECLUSAGE_BLENDINDICES: flag.BlendIndices = true; break;
      case D3DDECLUSAGE_NORMAL:       flag.Normal       = true; break;
      case D3DDECLUSAGE_PSIZE:        flag.PSize        = true; break;
      case D3DDECLUSAGE_TEXCOORD:     flag.TexCoord = pElement->UsageIndex+1; break;
      case D3DDECLUSAGE_TANGENT:      flag.Tangent      = true; break;
      case D3DDECLUSAGE_BINORMAL:     flag.BINormal     = true; break;
      case D3DDECLUSAGE_TESSFACTOR:   flag.TessFactor   = true; break;
      case D3DDECLUSAGE_POSITIONT:    flag.PositionT    = true; break;
      case D3DDECLUSAGE_COLOR:        flag.Color |= (1<<pElement->UsageIndex); break;
      case D3DDECLUSAGE_FOG:          flag.Fog          = true; break;
      case D3DDECLUSAGE_DEPTH:        flag.Depth        = true; break;
      case D3DDECLUSAGE_SAMPLE:       flag.Sample       = true; break;
    }
    pElement++;
  }
  return flag;
}*/


/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 構造体書き込み用
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ShaderBase::structDefines( char_cptr struct_name, _VARIABLE* pVariableTable, int variable_num )
{
  m_CodeBuf << "struct " << struct_name << " {\n";

  _VARIABLE* pVariable = pVariableTable;
  while( variable_num-- ){
    m_CodeBuf << pVariable->type << " " << pVariable->name;
    if( pVariable->semantics ){
      m_CodeBuf << " : " << pVariable->semantics;
    }
    m_CodeBuf << ";\n";
    pVariable++;
  }

  m_CodeBuf << "};\n";
}*/

/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 変数書き込み用？
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ShaderBase::variableDefines( char_cptr type, char_cptr name, char_cptr semantics, char_cptr init ){
#ifdef CPL_DEBUG
  // 変数タイプのチェック
//  String tmp(type);
//  tmp.C
#endif
  m_CodeBuf << type << " " << name;
  if( semantics ){
    m_CodeBuf << " : " << semantics;
  }
  if( init ){
    m_CodeBuf << " = " << init;
  }
  m_CodeBuf << ";\n";
}*/
/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 変数書き込み用？ 配列バージョン
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ShaderBase::variableDefinesArray( char_cptr type, char_cptr name, int num, char_cptr semantics, char_cptr init ){
  m_CodeBuf << type << " " << name << "[" << num << "]";
  if( semantics ){
    m_CodeBuf << " : " << semantics;
  }
  if( init ){
    m_CodeBuf << " = " << init;
  }
  m_CodeBuf << ";\n";
}*/

















//// _____________________________________________________________________________
//// 定数テーブルフラグメント簡易定義用マクロ　単体データ用
//// _____________________________________________________________________________
//#define CPL_DefinedMacro_CTAF(TYPE, FUNCTION) \
///* 明示的なインスタンス生成 */\
//template class ConstantTableAccessFragment<TYPE>;\
///* コールバック関数定義 */\
//void\
//  ConstantTableAccessFragment< TYPE >::CallSetConstantTable(\
//    DXDevice*  in_ptr_d3d_dev ) const\
//{\
//  m_ptr_constant_table->FUNCTION( in_ptr_d3d_dev, m_name, m_data );\
//}
//
//// _____________________________________________________________________________
//// 定数テーブルフラグメント簡易定義用マクロ　単体データ用（キャスト付き）
//// _____________________________________________________________________________
//#define CPL_DefinedMacro_CTAF2(TYPE, CAST_TYPE, FUNCTION) \
///* 明示的なインスタンス生成 */\
//template class ConstantTableAccessFragment<TYPE>;\
///* コールバック関数定義 */\
//void\
//  ConstantTableAccessFragment< TYPE >::CallSetConstantTable(\
//    DXDevice*  in_ptr_d3d_dev ) const\
//{\
//  m_ptr_constant_table->FUNCTION(\
//    in_ptr_d3d_dev, m_name, reinterpret_cast<CAST_TYPE>(m_data) );\
//}
//
//// _____________________________________________________________________________
//// 定数テーブルフラグメント簡易定義用マクロ　配列または長さ指定データ用
//// _____________________________________________________________________________
//#define CPL_DefinedMacro_CTAF_Array(TYPE, FUNCTION) \
///* 明示的なインスタンス生成 */\
//template class ConstantTableAccessFragment<TYPE>;\
///* コールバック関数定義 */\
//void\
//  ConstantTableAccessFragment< TYPE >::CallSetConstantTable(\
//    DXDevice*  in_ptr_d3d_dev ) const\
//{\
//  m_ptr_constant_table->FUNCTION( in_ptr_d3d_dev, m_name, m_data, m_data_num );\
//}
//
//// _____________________________________________________________________________
//// 定数テーブルフラグメント簡易定義用マクロ　配列または長さ指定データ用（キャスト付き）
//// _____________________________________________________________________________
//#define CPL_DefinedMacro_CTAF_Array2(TYPE, CAST_TYPE, FUNCTION) \
///* 明示的なインスタンス生成 */\
//template class ConstantTableAccessFragment<TYPE>;\
///* コールバック関数定義 */\
//void\
//  ConstantTableAccessFragment< TYPE >::CallSetConstantTable(\
//    DXDevice*  in_ptr_d3d_dev ) const\
//{\
//  m_ptr_constant_table->FUNCTION(\
//    in_ptr_d3d_dev, m_name, reinterpret_cast<CAST_TYPE>(m_data), m_data_num );\
//}
//
//
//// 明示的なインスタンス生成
////CPL_DefinedMacro_CTAF( BOOL, SetBoolArray );
//CPL_DefinedMacro_CTAF( float, SetFloat );
//CPL_DefinedMacro_CTAF_Array( float*, SetFloatArray );
//CPL_DefinedMacro_CTAF_Array( float const*, SetFloatArray );
//CPL_DefinedMacro_CTAF( int, SetInt );
//CPL_DefinedMacro_CTAF_Array( int*, SetIntArray );
//CPL_DefinedMacro_CTAF_Array( int const*, SetIntArray );
////CPL_DefinedMacro_CTAF( D3DXMATRIX const*, SetMatrix );
//CPL_DefinedMacro_CTAF_Array( D3DXMATRIX*, SetMatrixArray );
//CPL_DefinedMacro_CTAF_Array( D3DXMATRIX const*, SetMatrixArray );
//CPL_DefinedMacro_CTAF_Array( D3DXMATRIX const**, SetMatrixPointerArray );
////CPL_DefinedMacro_CTAF_Array2( Matrix const*,  D3DXMATRIX const*,  SetMatrixArray );
////CPL_DefinedMacro_CTAF_Array2( Matrix const**, D3DXMATRIX const**, SetMatrixPointerArray );
//CPL_DefinedMacro_CTAF_Array( void*, SetValue );
//CPL_DefinedMacro_CTAF_Array( void const*, SetValue );
//CPL_DefinedMacro_CTAF( D3DXVECTOR4*, SetVector );
//CPL_DefinedMacro_CTAF( D3DXVECTOR4 const*, SetVector );
////CPL_DefinedMacro_CTAF( float4, SetVector );


/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// フラグメント登録
// ※ baseConstantTableAccessFragmentポインタ指定バージョン
// 既に同じ名前があれば、データを上書きする。
// 引数
//    in_sp_frgm : 既に作成されたフラグメントのポインタ
// 戻り値
//    baseConstantTableAccessFragment::SpLinkedを返す。
//    引数 in_sp_frgm がそのまま返される。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ConstantTableAccessManager::SP_CTAF
  ConstantTableAccessManager::EntryFragment( SP_CTAF in_sp_frgm )
{
  int idx = FindFragment_by_Name( in_sp_frgm->GetName() );
  if( idx < 0 ) // 該当なし（新規登録）
  {
    m_CTAF_table.AddEnd( in_sp_frgm );
  }
  else // 該当あり（データ書き換え）
  {
    m_CTAF_table[ idx ] = in_sp_frgm;
  }

  return in_sp_frgm;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 指定した名前のフラグメントがあるかどうかの検索
// 該当するものがあればそのフラグメントポインタを返し、なければnullptrを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int
  ConstantTableAccessManager::FindFragment_by_Name( D3DXHANDLE in_name ) const
{
  int num = m_CTAF_table.GetNum();
  for( int i = 0; i < num; ++i )
  {
    if( ::strcmp( m_CTAF_table[ i ]->GetName(), in_name ) == 0 )
    {
      return i;
    }
  }
  return -1;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 定数テーブルアクセスマネージャ　定数テーブルに設定
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  ConstantTableAccessManager::SetConstantData(
    DXDevice*  in_ptr_d3d_dev ) const
{
  int num = m_CTAF_table.GetNum();
  for( int i = 0; i < num; ++i )
  {
    m_CTAF_table[ i ]->CallSetConstantTable( in_ptr_d3d_dev );
  }
}
*/


} // namespace Shader
} // namespace DX
} // namespace CPL