/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2008?/X/X - 2016/12/08
/// 
/// @brief  DirectX シェーダー用フラグメント関連。
/// 
/// ****************************************************************************

#pragma once

#include "CPL_DX_Shader_Keywords.h"
#include "CPL_DX_Shader_CodeItemFunction.h"

namespace CPL {
namespace DX {
namespace Shader {



class CodeFragment;

// _____________________________________________________________________________
/// @brief フラグメントチェインクラス
/// 
/// シェーダーコードの部品となるシェーダコードフラグメントのテーブルを管理します。
///
// *****************************************************************************
class CodeFragmentCollection
  : public Core::BaseObject
{
private:
  friend class CodeFragmentCollection;

  typedef std::shared_ptr<CodeFragment>        SpCodeFragment;
  typedef Core::Array::Dynamic<SpCodeFragment> CodeFragmentTable;
protected:
  //CPL_SINGLETON_INIT( Core::Memory::BlockPool<CodeFragmentCollection>, sm_scf_table, 64 )

  CodeFragmentTable m_sp_fragment_table;

public:
  // _____________________________________________________________________________
  /// @brief コンストラクタ
  /// 
  /// @tparam T_Fragment : CodeFragmentの派生クラスとなる必要があります。
  /// @param[in] in_ref_SCF : フラグメントを指定します。
  /// 
  /// @return 
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_Fragment>
  CodeFragmentCollection( cref<T_Fragment> in_ref_SCF )
  {
    m_sp_fragment_table.AddEnd(
      SpCodeFragment( new T_Fragment(in_ref_SCF) ) );
  }

  // _____________________________________________________________________________
  /// @brief デフォルトコンストラクタ ※ 何もしません。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  CodeFragmentCollection(){}

  // _____________________________________________________________________________
  /// @brief コピーコンストラクタ
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  CodeFragmentCollection( cref<CodeFragmentCollection> in_CFC )
    : m_sp_fragment_table( in_CFC.m_sp_fragment_table )
  {}
  // _____________________________________________________________________________
  /// @brief デストラクタ
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ~CodeFragmentCollection(){}

  // _____________________________________________________________________________
  /// @brief フラグメント数の取得
  /// 
  /// @return 保持するフラグメントの数を返します。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  size_t GetFragmentNum() const { return m_sp_fragment_table.GetNum(); }

  // _____________________________________________________________________________
  /// @brief フラグメントの取得
  /// 
  /// @param[in] in_index : 取得したいフラグメントのインデックスを指定します。
  /// 
  /// @return シェーダコードフラグメントを指すスマートポインタを返します。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  cref<SpCodeFragment>
    GetFragment( _in<size_t> in_index ) const
  {
    return m_sp_fragment_table[ in_index ];
  }

  // _____________________________________________________________________________
  /// @brief フラグメント登録
  /// 
  /// @tparam T_Fragment : CodeFragmentの派生クラスとなる必要があります。
  /// @param[in] in_fragment
  ///   : 登録するシェーダコードフラグメントを指定します。
  /// 
  /// @retval true  : 登録成功
  /// @retval false : 登録失敗
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_Fragment>
  bool Entry( cref<T_Fragment> in_fragment )
  {
    SpCodeFragment
      copy_frgm = CPL_MAKE_SHARED( T_Fragment, in_fragment );
    return ( m_sp_fragment_table.AddEnd( copy_frgm ) != nullptr );
  }
  
  // _____________________________________________________________________________
  /// @brief フラグメント登録
  /// 
  /// @tparam T_Fragment : CodeFragmentの派生クラスとなる必要があります。
  /// @param[in] in_fragment
  ///   : 登録するシェーダコードフラグメントを指定します。
  /// 
  /// @return 自身への参照を返します。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_Fragment>
  CodeFragmentCollection& operator << ( cref<T_Fragment> in_fragment )
  {
    Entry( in_fragment );
    return *this;
  }

  // _____________________________________________________________________________
  /// @brief フラグメント登録
  /// 
  /// @tparam T_Fragment : CodeFragmentの派生クラスとなる必要があります。
  /// @param[in] in_fragment
  ///   : 登録するシェーダコードフラグメントを指定します。
  /// 
  /// @return 登録後のコピーを返します。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_Fragment>
  CodeFragmentCollection operator + ( T_Fragment const& in_fragment )
  {
    return CodeFragmentCollection( *this ) << in_fragment;
  }
  
  // _____________________________________________________________________________
  /// @copydoc CodeFragmentCollection::operator+
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_Fragment>
  CodeFragmentCollection operator , ( T_Fragment const& in_fragment )
  {
    return CodeFragmentCollection( *this ) << in_fragment;
  }
};



// _____________________________________________________________________________
/// @brief シェーダコードマネージャー基底クラス
///
// *****************************************************************************
class CodeFragment
  : public CodeKeywords
{
protected:
  static css16 input, output;

  // 自身を登録した、CodeFragmentCollectionを取得する。
  // ただし自身を取得する際、継承先の型で指定する必要があるため、仮想関数で呼び出し、
  // 呼び出し先で自身を設定し、そのCodeFragmentCollectionを返す。
  virtual void GetThisFragmentCollection( CodeFragmentCollection& out_CFC ) const = 0;

  CodeFragment()
  //  : CodeCommand( in_ptr_code_buf )
  {}
  CodeFragment( InitializeVT_ const& in_ivt )
  //  : CodeCommand( nullptr )
  {}

private:
  static void zzf_typeName_to_SerialName_( char_ptr out_funcname, char_cptr in_typename );
protected:
  css128 zzf_GetFunctionName() const
  {
    char func_name[ 256 ] = { 'F','u','n','c','_' };
    zzf_typeName_to_SerialName_( func_name+5, typeid(*this).name() );
    return func_name;
  }
  css128 zzf_GetStructName() const
  {
    char func_name[ 256 ] = { 'S','t','r','u','c','t','_' };
    zzf_typeName_to_SerialName_( func_name+7, typeid(*this).name() );
    return func_name;
  }

public:
  virtual ~CodeFragment(){}

  virtual void On_SetupGlobalParameter( rout<CIV_Global     > out_global_param ){}
  virtual void On_SetupInputParameter ( rout<CIV_Input      > out_input_param  ){}
  virtual void On_SetupOutputParameter( rout<CIV_Output     > out_output_param ){}
  virtual void On_SetupFunction       ( rout<CIFunctionSet  > out_functions, _in<CIV_Global> in_global_param ){}
  virtual void On_SetupInitializer    ( rout<CIV_Initializer> out_initialize_param ){}
  virtual void On_CallFunction        ( rout<ss             > out_statement, _in<CodeItemSet> in_parameter ){}
  virtual void On_SetupFinalizer      ( _in <CIV_Finalizer  > in_finalize_param ){}

  // _____________________________________________________________________________
  /// @brief フラグメント連結
  /// 
  /// @tparam T_Fragment : CodeFragmentの派生クラスとなる必要があります。
  /// @param[in] in_fragment
  ///   : 連結するシェーダコードフラグメントを指定します。
  /// 
  /// @return フラグメントを追加したコピーを返します。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_Fragment>
  CodeFragmentCollection operator + ( cref<T_Fragment> in_fragment )
  {
    CodeFragmentCollection cfc;
    GetThisFragmentCollection( cfc );
    return cfc << in_fragment;
  }
  
  // _____________________________________________________________________________
  /// @copydoc operator+
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_Fragment>
  CodeFragmentCollection operator , ( cref<T_Fragment> in_fragment )
  {
    CodeFragmentCollection cfc;
    GetThisFragmentCollection( cfc );
    return cfc << in_fragment;
  }
};



#define CPL_DEF_GET_THIS_FRAGMENT_COLLECTION\
    void GetThisFragmentCollection( CodeFragmentCollection& out_CFC ) const override\
    {\
      out_CFC << *this;\
    }




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