/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/12/07
/// 
/// @brief  DirectX シェーダーコード基底アイテムクラス
/// 
/// ****************************************************************************

#pragma once

#include "CPL_DX_Shader_Keywords.h"

namespace CPL {
namespace DX {
namespace Shader {


// _____________________________________________________________________________
/// @brief シェーダーコードアイテム
///
// *****************************************************************************
class CodeItem
  : public CodeKeywords
{
private:
public:
  struct Parameter_ {
    Core::StringA             m_str;
    mutable Parameter_ const* m_ptr_next_param;

    Parameter_( char_cptr in_str, bool in_f_token );
    virtual ~Parameter_()
    {}
    operator char_cptr() const { return m_str; }

    Parameter_& operator | ( Parameter_ const& in_param );
    Parameter_& operator / ( Parameter_ const& in_param ){ return (*this)|in_param; }
    Parameter_& operator + ( Parameter_ const& in_param ){ return (*this)|in_param; }
    
    Parameter_& operator []( int n ){ m_str += n; return *this; } // 文字列としての数値が加算される

    Parameter_& operator = ( char_cptr in_str )
    {
      m_str = in_str;
      return *this;
    }

    bool IsEmpty() const { return m_str == ""; }
  };

  template<int I, int IS_TOKEN>
  struct ParameterT_ : Parameter_
  {
    ParameterT_( char_cptr in_str, bool in_f_token = IS_TOKEN )
      : Parameter_( in_str, in_f_token )
    {}
    ~ParameterT_()
    {}
  };

  CPL_SCOPED_ENUM
    ( eParamType, int
    , Unkown
    , StrageClass
    , TypeModifier
    , InputModifier
    , Type
    , Name
    , Semantic
    , Annotation
    , Initialize
    , PackOffset
    , Register
    , StatementBlock
    , Interpolation
    );

public:
  using NonParam_      = ParameterT_<eParamType::Unkown,        false>;
  using StrageClass    = ParameterT_<eParamType::StrageClass,   true >;
  using TypeModifier   = ParameterT_<eParamType::TypeModifier,  true >;
  using InputModifier  = ParameterT_<eParamType::InputModifier, true >;
  using Type           = ParameterT_<eParamType::Type,          true >;
  using Name           = ParameterT_<eParamType::Name,          true >;
  using Semantic       = ParameterT_<eParamType::Semantic,      true >;
  using Annotation     = ParameterT_<eParamType::Annotation,    false>;
  using Initialize     = ParameterT_<eParamType::Initialize,    false>;
  using PackOffset     = ParameterT_<eParamType::PackOffset,    false>;
  using Register       = ParameterT_<eParamType::Register,      false>;
  using StatementBlock = ParameterT_<eParamType::StatementBlock,false>;
  using Interpolation  = ParameterT_<eParamType::Interpolation, false>;
  struct Num : Parameter_ // 配列数
  {
    int num;
    Num( int in_num )
      : Parameter_(css16(in_num),false)
      , num(in_num)
    {}
  };

  virtual bool operator == ( CodeItem const& in_item ) const = 0;
  virtual bool operator != ( CodeItem const& in_item ) const
  {
    return !((*this) == in_item);
  }

  CPL_SCOPED_ENUM
    ( eCreateType, int
    , Global
    , Structure
    , Argument
    , ArgumentEnd
    , Function
    , Local
    );
  virtual void MakeCode( rout<ss> out_buf, _in<eCreateType> in_create_type ) const = 0;
  //virtual CodeItem*       GetSubCast( CodeItem* in_item_pointer ) = 0;
  //virtual CodeItem const* GetSubCast( CodeItem const* in_item_pointer ) const = 0;

  virtual char_cptr GetStrageClass()    const = 0;
  virtual char_cptr GetTypeModifier()   const = 0;
  virtual char_cptr GetInputModifier()  const = 0;
  virtual char_cptr GetType()           const = 0;
  virtual char_cptr GetName()           const = 0;
  virtual char_cptr GetSemantic()       const = 0;
  virtual char_cptr GetAnnotation()     const = 0;
  virtual char_cptr GetInitilize()      const = 0;
  virtual char_cptr GetPackOffset()     const = 0;
  virtual char_cptr GetRegister()       const = 0;
  virtual char_cptr GetStatementBlock() const = 0;
  virtual char_cptr GetInterpolation()  const = 0;
  virtual int       GetNum()            const = 0;
};


//typedef sdptr<CodeItem> SpCodeItem;

typedef CodeItem::StrageClass     CPStrageClass;
typedef CodeItem::TypeModifier    CPTypeModifier;
typedef CodeItem::InputModifier   CPInputModifier;
typedef CodeItem::Type            CPType;
typedef CodeItem::Name            CPName;
typedef CodeItem::Semantic        CPSemantic;
typedef CodeItem::Annotation      CPAnnotation;
typedef CodeItem::Initialize      CPInitialize;
typedef CodeItem::PackOffset      CPPackOffset;
typedef CodeItem::Register        CPRegister;
typedef CodeItem::StatementBlock  CPStatementBlock;
typedef CodeItem::Interpolation   CPInterpolation;
typedef CodeItem::Num             CPNum;


// _____________________________________________________________________________
/// @brief コードアイテムセット
///
// *****************************************************************************
class CodeItemSet
  : public CodeItem
{
private:
  char_cptr GetStrageClass()    const { return ""; }
  char_cptr GetTypeModifier()   const { return ""; }
  char_cptr GetInputModifier()  const { return ""; }
  char_cptr GetType()           const { return ""; }
  char_cptr GetName()           const { return ""; }
  char_cptr GetSemantic()       const { return ""; }
  char_cptr GetAnnotation()     const { return ""; }
  char_cptr GetInitilize()      const { return ""; }
  char_cptr GetPackOffset()     const { return ""; }
  char_cptr GetRegister()       const { return ""; }
  char_cptr GetStatementBlock() const { return ""; }
  char_cptr GetInterpolation()  const { return ""; }
  int       GetNum()            const { return 0; }
protected:
  typedef std::vector< sdptr<CodeItem> > SpCodeItemArray;

  SpCodeItemArray m_array_item;
public:
  CodeItemSet();
  CodeItemSet( cref<CodeItemSet> in_item_set );
  ~CodeItemSet();
  
  // _____________________________________________________________________________
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  bool CompItem( _in<CodeItem> in_item ) const;

  // _____________________________________________________________________________
  /// @brief アイテム登録（スマートポインタversion）
  /// 
  /// @param[in] in_sp_item
  ///   : 登録したいコードアイテムを指すスマートポインタを指定します。\n
  ///   : 既に一致するアイテムが存在する場合は登録されません。
  /// 
  /// @retval true  : 登録成功。
  /// @retval false : 登録失敗。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_Item>
  bool Entry( cref<sdptr<T_Item>> in_sp_item )
  {
    try {
      if( CompItem( *in_sp_item ) ) return false; // 一致した場合抜ける。CodeItem派生クラスでない場合もここでエラーとなる。
      m_array_item.push_back( in_sp_item );
      return true;
    } catch(...) {
      return false;
    }
  }

  // _____________________________________________________________________________
  /// @brief アイテム登録
  /// 
  /// @param[in] in_item
  ///   : 登録したいコードアイテムを指定します。\n
  ///   : 既に一致するアイテムが存在する場合は登録されません。
  /// 
  /// @retval true  : 登録成功。
  /// @retval false : 登録失敗。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_Item>
  bool Entry( cref<T_Item> in_item )
  {
    try {
      if( CompItem( in_item ) ) return false; // 一致した場合抜ける。CodeItem派生クラスでない場合もここでエラーとなる。
      sdptr<CodeItem> sp_item( CPL_MAKE_SHARED( T_Item, in_item ) );
      m_array_item.push_back( sp_item );
      return true;
    } catch(...) {
      return false;
    }
  }
  
// _____________________________________________________________________________
/// @copydoc Entry(cref<T_Item>)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_Item>
  CodeItemSet& operator << ( cref<T_Item> in_item )
  {
    Entry( in_item );
    return *this;
  }

  // _____________________________________________________________________________
  /// @brief アイテム数取得。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  size_t GetItemNum() const { return m_array_item.size(); }

  // _____________________________________________________________________________
  /// @brief アイテム取得。
  /// 
  /// @param[in] in_index : 取得するアイテムのインデックス。
  /// 
  /// @return アイテムの指すスマポを返す。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  sdptr<CodeItem>
    GetItem( size_t in_index ) const { return m_array_item[ in_index ]; }

  // _____________________________________________________________________________
  /// @brief 型を指定したアイテムの検索
  /// 
  /// 指定した型の指定した位置のアイテムを検索します。
  /// 
  /// @tparam T_Item
  ///   : 一致比較する型を指定します。\n
  ///   : 指定する型はCodeItemの派生クラスになります。
  /// @param[in] in_offs_idx
  ///   : 取得したい位置を指定できます。\n
  ///   : 同一の型におけるオフセットを指定します。
  /// 
  /// @return
  /// 一致したコードアイテムのスマートポインタを返します。\n
  /// 一致しない場合はnullptr相当のスマートポインタを返します。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_Item>
  sdptr<T_Item>
    FindItem( _in<size_t> in_offs_idx = 0 ) const
  {
    auto offs_count = in_offs_idx;
    //CPL_LOOP2( GetItemNum(), item_idx )
    for( auto& sp_item : m_array_item )
    {
      T_Item const* ptr_item = SmartCast<T_Item const*>( sp_item );
      if( ptr_item )
      {
        if( offs_count == 0 )
        {
          return std::dynamic_pointer_cast<T_Item>(sp_item);
        }
        offs_count--;
      }
    }
    return nullptr;
  }

  // _____________________________________________________________________________
  /// @brief 型およびパラメーターを指定してアイテム検索（最大３パラメーター）
  /// 
  /// 引数に指定して有効なのはTypeModifier,InputModifier,Type,Name,Semanticのみ。\n
  /// これら以外は無視されます。\n
  /// 
  /// @return
  /// 一致したコードアイテムのスマートポインタを返します。\n
  /// 一致しない場合はnullptr相当のスマートポインタを返します。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T_Item>
  sdptr<T_Item>
    FindItem
      ( cref<Parameter_> in_param0
      , cref<Parameter_> in_param1 = NonParam_("")
      , cref<Parameter_> in_param2 = NonParam_("")
      ) const
  {
    SDEF search_index = 0;
    while( true )
    {
      // パラメーターと一致するアイテムを検索
      search_index =
        zzf_FindItemIndex( search_index, in_param0, in_param1, in_param2 );
      if( !FAILED_INDEX(search_index) )
      { // ヒットしたアイテムがキャスト可能かチェック
        if( SmartCast<T_Item const*>( m_array_item[ search_index ] ) )
        {
          // キャスト可能ならそのアイテムを返す。
          return std::dynamic_pointer_cast<T_Item>(m_array_item[ search_index ]);
        }
      }
      else
      {
        break;
      }
      ++search_index; // 次の要素へ
    }
    return nullptr; // 該当なし
  }
      
  // _____________________________________________________________________________
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  bool operator == ( _in<CodeItem> in_item ) const;
  
  // _____________________________________________________________________________
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  void MakeCode( rout<ss> out_buf, _in<eCreateType> in_create_type ) const;

private:
  SDEF
    zzf_FindItemIndex
      ( _in <size_t    > in_start_index
      , cref<Parameter_> in_param0
      , cref<Parameter_> in_param1
      , cref<Parameter_> in_param2
      ) const;
};


// _____________________________________________________________________________
/// @brief アイテム集合テンプレート化クラス
///
// *****************************************************************************
template<int N>
class CodeItemSetN : public CodeItemSet
{
public:
  CodeItemSetN(){}
  CodeItemSetN( CodeItemSetN const& in_CIS )
    : CodeItemSet( in_CIS )
  {}
};



CPL_SCOPED_ENUM
  ( eCodeItemType, int
  , Global
  , Input
  , Output
  , Initializer
  , Finalizer
  , Function
  );


//
typedef CodeItemSetN<eCodeItemType::Global>      CIV_Global;
typedef CodeItemSetN<eCodeItemType::Initializer> CIV_Initializer;
typedef CodeItemSetN<eCodeItemType::Finalizer>   CIV_Finalizer;
typedef CodeItemSetN<eCodeItemType::Function>    CIFunctionSet;


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