/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2015/11/14
/// 
/// @brief  DirectX シェーダーコードクラス関連。
/// 
/// ****************************************************************************


//#include "CPL_DX_ShaderCodeItem.h"
#include "CPL_DX_Shader_CodeItemVariable.h"

namespace CPL {
namespace DX {
namespace Shader {



// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// コードパラメーター　コンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// _____________________________________________________________________________
/// @brief コンストラクタ
/// 
/// @param[in] in_str : 
/// @param[in] in_f_token : 
/// 
/// @return 
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CodeItem::Parameter_::Parameter_( _in<char_cptr> in_str, _in<bool> in_f_token )
  : m_str( ReserveSize(16) )
  , m_ptr_next_param( nullptr )
{
  if( in_f_token )
  {
    // 最初のトークンのみ検出する（スペースが含まれる場合があるため）
    char token[ 256 ] = { 0 }; // 最大256文字
    int  token_idx = 0;
    char_cptr s = in_str;
    while( *s )
    {
      // アルファベット（大文字、小文字）・数字・アンダーバーの場合のみ書き込む
      if( *s == '_'
       || ('a' <= *s  &&  *s <= 'z')
       || ('A' <= *s  &&  *s <= 'Z')
       || ('0' <= *s  &&  *s <= '9') )
      {
        token[ token_idx ] = *s;
        ++token_idx;
      }
      ++s;
    }

    // トークン文字列設定
    m_str = token;
  }
  else
  {
    m_str = in_str;
  }
}


// _____________________________________________________________________________
/// @brief 連結用
/// 
/// @return 自身への参照を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CodeItem::Parameter_&
  CodeItem::Parameter_::operator | ( Parameter_ const& in_param )
{
  Parameter_ const* ptr_param_end = this;
  while( ptr_param_end->m_ptr_next_param )
  {
    ptr_param_end = ptr_param_end->m_ptr_next_param;
  }

  ptr_param_end->m_ptr_next_param = &in_param;

  return *this;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 指定したパラメータと一致する変数アイテムを取得
// ※ 引数に指定して有効なのはTypeModifier,InputModifier,Type,Name,Semanticのみ。
//    これら以外は無視される。
// ※ パラメータは１つ以上指定されなければならない。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF
  CodeItemSet::zzf_FindItemIndex
    ( _in <size_t    > in_start_index
    , cref<Parameter_> in_param0
    , cref<Parameter_> in_param1
    , cref<Parameter_> in_param2
    ) const
{
  const size_t item_num = GetItemNum();
  if( in_start_index >= item_num )
    return -1;

  Parameter_ const
    *type_modifier  = nullptr,
    *input_modifier = nullptr,
    *type           = nullptr,
    *name           = nullptr,
    *semantic       = nullptr;
  
  Parameter_ const* params[] = { &in_param0, &in_param1, &in_param2 };
  //const int param_num = CPL_ARRAY_NUM(in_param);
  //CPL_LOOP2( param_num, param_idx )
  for( auto ptr_param : params )
  {
    Parameter_ const& param = *ptr_param;
    if     ( typeid(param) == typeid(NonParam_)     ) break;
    else if( typeid(param) == typeid(TypeModifier)  ) type_modifier  = ptr_param;
    else if( typeid(param) == typeid(InputModifier) ) input_modifier = ptr_param;
    else if( typeid(param) == typeid(Type)          ) type           = ptr_param;
    else if( typeid(param) == typeid(Name)          ) name           = ptr_param;
    else if( typeid(param) == typeid(Semantic)      ) semantic       = ptr_param;
  }


  for( size_t item_idx = in_start_index; item_idx < item_num; ++item_idx )
  {
    auto sp_item = GetItem( item_idx );

    bool f_type_modifier  = true,
         f_input_modifier = true,
         f_type           = true,
         f_name           = true,
         f_semantic       = true;
    // type modifier
    if( type_modifier )
    {
      f_type_modifier =
        strncmp
          ( sp_item->GetTypeModifier()
          , *type_modifier
          , strlen(*type_modifier)
          ) == 0;
    }
    // input modifier
    if( input_modifier )
    {
      f_input_modifier =
        strncmp
          ( sp_item->GetInputModifier()
          , *input_modifier
          , strlen(*input_modifier)
          ) == 0;
    }
    // 型
    if( type )
    {
      f_type = strncmp( sp_item->GetType(), *type, strlen(*type) ) == 0;
    }
    // 名前
    if( name )
    {
      f_name = strncmp( sp_item->GetName(), *name, strlen(*name) ) == 0;
    }
    // セマンティック
    if( semantic )
    {
      f_semantic =
        strncmp
          ( sp_item->GetSemantic()
          , *semantic
          , strlen(*semantic)
          ) == 0;
    }

    if( f_type_modifier  &&  f_type  &&  f_name  &&  f_semantic )
    {
      return item_idx;
    }
  } // item_idx

  return -1;
}







// _____________________________________________________________________________
/// @brief デフォルトコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CodeItemSet::CodeItemSet()
{
  // 今のところ何もしない
}


// _____________________________________________________________________________
/// @brief コピーコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CodeItemSet::CodeItemSet( cref<CodeItemSet> in_item_set )
{
}


// _____________________________________________________________________________
/// @brief デストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CodeItemSet::~CodeItemSet()
{
  // 今のところ何もしない
}


// _____________________________________________________________________________
/// @brief 保持アイテムとの比較
/// 
/// 保持しているアイテムと、指定されたアイテムの名前を比較します。
/// 
/// @param[in] in_item : 比較するコードアイテムを指定します。
/// 
/// @retval true  : １つ以上の一致あり。
/// @retval false : 一致なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool CodeItemSet::CompItem( _in<CodeItem> in_item ) const
{
  const size_t item_num = GetItemNum();
  if( item_num == 0 )
  {
    return false;
  }
  // チェックループ
  //CPL_LOOP2( item_num, item_idx )
  for( auto& sp_item : m_array_item )
  {
    if( *sp_item == in_item )
    {
      return true;
    }
  }

  return false;
}


// _____________________________________________________________________________
/// @brief 比較
/// 
/// @param[in] in_item : 比較したいコードアイテムを指定します。
/// 
/// @retval true  : 一致あり。
/// @retval false : 一致なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool CodeItemSet::operator == ( _in<CodeItem> in_item ) const
{
  // 自身と同じ型かどうかチェック（派生クラスであれば派生クラスとして判定される）
  if( typeid(this) != typeid(&in_item) )
  {
    return false;
  }
  // ↑同じ型なので、CodeItemSetポインタを取得
  CodeItemSet const*
    ptr_item_set = dynamic_cast<CodeItemSet const*>( &in_item );
  if( !ptr_item_set )
  {
    return false;
  }

  size_t num = GetItemNum();
  if( num != ptr_item_set->GetItemNum() )
  {
    return false;
  }

  // アイテム比較
  CPL_LOOP2( num, item_idx )
  {
    auto& sp_item = m_array_item[ item_idx ];
    if( *sp_item != *ptr_item_set->GetItem( item_idx ) )
    {
      return false;
    }
  }

  return true;
}


// _____________________________________________________________________________
/// @brief アイテムのコード定義呼び出し
/// 
/// @param[out] out_buf
///   : シェーダコード格納用の文字列オブジェクトを渡します。
/// @param[in] in_create_type
///   : 生成するコードのタイプを指定します。\n
///   : 次のフラグが指定可能です。\n
///   : eCreateType::Global      - グローバル変数\n
///   : eCreateType::Structure   - 構造体        \n
///   : eCreateType::Argument    - 引数          \n
///   : eCreateType::ArgumentEnd - 最後の引数    \n
///   : eCreateType::Function    - 関数          \n
///   : eCreateType::Local       - ローカル変数  \n
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void CodeItemSet::MakeCode( rout<ss> out_buf, _in<eCreateType> in_create_type ) const
{
  size_t item_num = m_array_item.size();
  CPL_LOOP2( item_num, item_idx )
  {
    if( in_create_type == eCreateType::Argument  &&  item_idx == item_num-1 )
    {
      GetItem( item_idx )->MakeCode( out_buf, eCreateType::ArgumentEnd );
    }
    else
    {
      GetItem( item_idx )->MakeCode( out_buf, in_create_type );
    }
  }
}


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


