#pragma once

/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2016/08/23 - 2016/08/23
/// 
/// @brief  CorePLi オリジナルタグ関連ヘッダー
/// 
/// ※ マークアップ的な記述用にオリジナルフォーマットを定義
/// 
/// ****************************************************************************

#include "../Pointer/CPL_Core_Pointer.h"
#include "../Base/CPL_Core_BasicVector.h"
#include "../Base/CPL_Core_LexicalAnalysis.h"
#include "../Stream/CPL_Core_Stream.h"

namespace CPL {
namespace Core {

namespace zzz {
  void eraseAfterSpace_( rio<std::string> io_str );
  void eraseBeforeSpace_( rio<std::string> io_str );
  void eraseSideSpace_( rio<std::string> io_str );

  // _____________________________________________________________________________
  /// @brief タグオブジェクト
  // *****************************************************************************
  class Tag
    : public Core::IncludeSmartLinkTree<Tag>
  {
    friend class Tag;
    using td_base = Core::IncludeSmartLinkTree<Tag>;
    friend td_base;
  private:
    //static sdptr<Tag>& sm_tag_root(){
    //  static sdptr<Tag> obj{ CPL_MAKE_SHARED(Tag) };
    //  CPL_BLOCK_ONCE2
    //  {
    //    obj->SetLinkMyself( obj );
    //    return true;
    //  }();
    //  return obj;
    //}
    //CPL_SINGLETON_INIT( wkptr<Tag>, sm_tag_active, sm_tag_root() )

    std::string m_name;
    size_t      m_num_child{ 0 };

    sdptr<Tag> toData_( rvref<Tag> tag );
    sdptr<Tag> toData_( cref<Tag> tag );
    sdptr<Tag> toData_( pin<char> str );
    sdptr<Tag> toData_( rvref<std::string> str );
    sdptr<Tag> toData_( cref<std::string> str );
    //sdptr<Tag> toData_( int value );
    //sdptr<Tag> toData_( U32 value );
    //sdptr<Tag> toData_( S64 value );
    //sdptr<Tag> toData_( U64 value );
    //sdptr<Tag> toData_( float value );
    //sdptr<Tag> toData_( double value );
    sdptr<Tag> toData_( bool value );
    template<class T_Value>
    sdptr<Tag> toData_( T_Value value )
    {
      return AddTag( Tag{ std::to_string( value ) } );
    }

    template<class T, class... T_Params>
    void Expand_( T&& data0, T_Params&&... args )
    {
      Expand_( std::forward<T>(data0) );
      Expand_( std::forward<T_Params>(args)... );
    }
    template<class T>
    void Expand_( T&& data0 )
    {
      toData_( std::forward<T>(data0) );
    }
    void Expand_(){}

    static std::string StrIndent_( int in_indent );
    std::string Output_( bool is_root, int in_indent ) const;
    auto zzz_makeThis_()
    {
    #ifdef CPL_DBGSW_ALLOCATOR
      return sdptr<Tag>( this, Core::Memory::CustomDelete<Tag> );
    #else
      return sdptr<Tag>( this );
    #endif
    }
  public:
    //static auto GetTagRoot(){ return sm_tag_root(); }
    //static auto GetTagActive(){ return sm_tag_active().lock(); }

    static std::vector<sdptr<Tag>>
      Load( _in<Stream_i_> in_stream );
    static std::vector<sdptr<Tag>>
      Load
        ( pin<char  > in_source
        , _in<size_t> in_size = -1
        );
    static std::vector<sdptr<Tag>>
      Load( _in<std::string> in_source )
    {
      return Load( in_source.c_str(), in_source.size() );
    }

    void  SetName( _in<std::string> in_name ){ m_name = in_name; }
    auto& GetName()      const { return m_name; }
    auto& GetName()            { return m_name; }
    auto  GetTagPrev()   const { return GetLinkPrev(); }
    auto  GetTagNext()   const { return GetLinkNext(); }
    auto  GetTagChild()  const { return GetLinkChild(); }
    auto  GetTagParent() const { return GetLinkParent(); }
    auto  GetDataNum()   const { return m_num_child; }
    bool  IsDataTag()    const { return GetLinkChild() == nullptr; }
    bool  IsChildTag()   const { return GetLinkChild() != nullptr; }

    sdptr<Tag> AddTag( rvref<Tag> tag ){ return toData_(std::move(tag)); }
    sdptr<Tag> AddTag( cref<Tag>  tag ){ return toData_(tag); }
    sdptr<Tag> AddTag( sdptr<Tag> in_sp_tag )
    {
      return AddLinkChild( in_sp_tag );
    }
    bool
      Input
        ( rout<Tag   > io_tag_parent
        , pin <char  > in_source
        , _in <size_t> in_size = -1
        );
    bool
      Input
        ( rio<Tag> io_tag_parent
        , rio<Core::Tokenizer> io_tokenizer
        );

    //auto begin()  const { return this->td_base::begin();  }
    //auto begin()        { return this->td_base::begin();  }
    //auto end()    const { return this->td_base::end();    }
    //auto end()          { return this->td_base::end();    }
    //auto rbegin() const { return this->td_base::rbegin(); }
    //auto rbegin()       { return this->td_base::rbegin(); }
    //auto rend()   const { return this->td_base::rend();   }
    //auto rend()         { return this->td_base::rend();   }

    template<class... T_Params>
    static sdptr<Tag>
      Create( ref<Tag> io_tag_parent, T_Params&&... args )
    {
      auto sp_this =
        CPL_MAKE_SHARED( Tag, std::forward<T_Params>(args)... );
      sp_this->SetLinkMyself( sp_this );
      // ノードへ追加
      // ※ 子ノードに登録すれば、インスタンスが保持される。
      io_tag_parent.AddLinkChild( sp_this ); // ※ 仕様上必ず親は存在する。
      return sp_this;
    }
    
    template<class std_string, class... T_Params>
    Tag( std_string&& name_, T_Params&&... args )
      : m_name( name_ )
    {
      Expand_( std::forward<T_Params>(args)... );
    }

    Tag( rvref<Tag> tag )
      : td_base( std::move(tag) )
      , m_name ( std::move(tag.m_name) )
    {}
    Tag( cref<Tag> tag )
      : td_base( tag )
      , m_name ( tag.m_name )
    {}
    Tag(){}
    ~Tag(){}

    operator std::string() const
    {
      return Output_( true, 0 );
    }

    Tag& operator = ( rvref<Tag> tag )
    {
      m_name = std::move(tag.m_name);
      *static_cast<td_base*>(this) = static_cast<td_base&&>(tag);
      return *this;
    }

    // 名前設定用に特殊オーバーロード
    Tag& operator , ( _in<std::string> in_name ) &
    {
      m_name = in_name;
      return *this;
    }
    // 名前設定用に特殊オーバーロード
    Tag&& operator , ( _in<std::string> in_name ) &&
    {
      m_name = in_name;
      return std::move(*this);
    }
  };

} // namespace zzz

} // namespace Core

// Text Block Tag
using TBTag = Core::zzz::Tag;




// _____________________________________________________________________________
// 基本的な型のタグの読み書き
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#define CPL_DEF_CORE_TAG_RSHIFT_INOUT_(type_left,type_right)   \
inline auto& operator >> (cref<type_left> l, rout<type_right> r)\
{                                                              \
  r << l; return l;                                            \
}
#define CPL_DEF_CORE_TAG_RSHIFT_INOUT(type)\
CPL_DEF_CORE_TAG_RSHIFT_INOUT_(TBTag,type) \
CPL_DEF_CORE_TAG_RSHIFT_INOUT_(type,TBTag)

#define CPL_DEF_CORE_TAG_OPERATOR_INOUT_BASIC_VALUE(type, name, func)\
inline TBTag ToTag( type in_value ) \
{                                   \
  return TBTag{ name, in_value };   \
}                                   \
inline type& operator << ( rout<type> out_value, _in<TBTag> in_tag )\
{                                                                   \
  if( auto sp_item = in_tag.GetTagChild() )                         \
    out_value = static_cast<type>(func(sp_item->GetName()));        \
  return out_value;                                                 \
}                                                                   \
inline TBTag& operator << (rout<TBTag> out_tag, type in_value )\
{                                                              \
  out_tag = ToTag( in_value );                                 \
  return out_tag;                                              \
}                                                              \
CPL_DEF_CORE_TAG_RSHIFT_INOUT( type )


CPL_DEF_CORE_TAG_OPERATOR_INOUT_BASIC_VALUE(S8 , "S8"  , std::stoi)
CPL_DEF_CORE_TAG_OPERATOR_INOUT_BASIC_VALUE(U8 , "U8"  , std::stoi)
CPL_DEF_CORE_TAG_OPERATOR_INOUT_BASIC_VALUE(S16, "S16" , std::stoi)
CPL_DEF_CORE_TAG_OPERATOR_INOUT_BASIC_VALUE(U16, "U16" , std::stoi)
CPL_DEF_CORE_TAG_OPERATOR_INOUT_BASIC_VALUE(S32, "S32" , std::stoi)
CPL_DEF_CORE_TAG_OPERATOR_INOUT_BASIC_VALUE(U32, "U32" , std::stoi)
CPL_DEF_CORE_TAG_OPERATOR_INOUT_BASIC_VALUE(S64, "S64" , std::stoi)
CPL_DEF_CORE_TAG_OPERATOR_INOUT_BASIC_VALUE(U64, "U64" , std::stoi)
CPL_DEF_CORE_TAG_OPERATOR_INOUT_BASIC_VALUE(float  , "Float" , std::stof)
CPL_DEF_CORE_TAG_OPERATOR_INOUT_BASIC_VALUE(double , "Double", std::stod)

inline TBTag ToTag( _in<std::string> s ){ return TBTag{ "String", s }; }
inline TBTag& operator << ( rout<TBTag> tag, _in<std::string> s )
{
  tag = ToTag(s);
  return tag;
}
std::string& operator << ( rout<std::string> s, _in<TBTag> tag );
CPL_DEF_CORE_TAG_RSHIFT_INOUT( std::string )


// _____________________________________________________________________________
// vector系

#define CPL_DEF_CORE_TAG_INOUT_POINT2(type, name, func)\
inline TBTag ToTag( _in<type> in_vec2 )      \
{                                            \
  return TBTag{ name, in_vec2.x, in_vec2.y };\
}                                            \
inline type& operator << ( rout<type> out_value, _in<TBTag> in_tag )\
{                                                                   \
  auto sp_item = in_tag.GetTagChild();                              \
  if( sp_item ) out_value.x = func(sp_item->GetName()), sp_item = sp_item->GetTagNext();\
  if( sp_item ) out_value.y = func(sp_item->GetName());             \
  return out_value;                                                 \
}                                                                   \
inline TBTag& operator << ( rout<TBTag> tag, _in<type> in_value )\
{                                                                \
  tag = ToTag( in_value );                                       \
  return tag;                                                    \
}                                                                \
CPL_DEF_CORE_TAG_RSHIFT_INOUT(type)

#define CPL_DEF_CORE_TAG_INOUT_POINT3(type, name, func)\
inline TBTag ToTag( _in<type> in_vec3 )      \
{                                            \
  return TBTag{ name, in_vec3.x, in_vec3.y, in_vec3.z };\
}                                            \
inline type& operator << ( rout<type> out_value, _in<TBTag> in_tag )\
{                                                                   \
  auto sp_item = in_tag.GetTagChild();                              \
  if( sp_item ) out_value.x = func(sp_item->GetName()), sp_item = sp_item->GetTagNext();\
  if( sp_item ) out_value.y = func(sp_item->GetName()), sp_item = sp_item->GetTagNext();\
  if( sp_item ) out_value.z = func(sp_item->GetName());             \
  return out_value;                                                 \
}                                                                   \
inline TBTag& operator << ( rout<TBTag> tag, _in<type> in_value )\
{                                                                \
  tag = ToTag( in_value );                                       \
  return tag;                                                    \
}                                                                \
CPL_DEF_CORE_TAG_RSHIFT_INOUT(type)

#define CPL_DEF_CORE_TAG_INOUT_POINT4(type, name, func)\
inline TBTag ToTag( _in<type> in_vec4 )      \
{                                            \
  return TBTag{ name, in_vec4.x, in_vec4.y, in_vec4.z };\
}                                            \
inline type& operator << ( rout<type> out_value, _in<TBTag> in_tag )\
{                                                                   \
  auto sp_item = in_tag.GetTagChild();                              \
  if( sp_item ) out_value.x = func(sp_item->GetName()), sp_item = sp_item->GetTagNext();\
  if( sp_item ) out_value.y = func(sp_item->GetName()), sp_item = sp_item->GetTagNext();\
  if( sp_item ) out_value.z = func(sp_item->GetName()), sp_item = sp_item->GetTagNext();\
  if( sp_item ) out_value.w = func(sp_item->GetName());             \
  return out_value;                                                 \
}                                                                   \
inline TBTag& operator << ( rout<TBTag> tag, _in<type> in_value )\
{                                                                \
  tag = ToTag( in_value );                                       \
  return tag;                                                    \
}                                                                \
CPL_DEF_CORE_TAG_RSHIFT_INOUT(type)


#define CPL_DEF_CORE_TAG_INOUT_POINT_X( type, name, func )\
CPL_DEF_CORE_TAG_INOUT_POINT2( type##2, name"2", func )\
CPL_DEF_CORE_TAG_INOUT_POINT3( type##3, name"3", func )\
CPL_DEF_CORE_TAG_INOUT_POINT4( type##4, name"4", func )

CPL_DEF_CORE_TAG_INOUT_POINT_X( S8x , "S8x" , std::stoi )
CPL_DEF_CORE_TAG_INOUT_POINT_X( U8x , "U8x" , std::stoi )
CPL_DEF_CORE_TAG_INOUT_POINT_X( S16x, "S16x", std::stoi )
CPL_DEF_CORE_TAG_INOUT_POINT_X( U16x, "U16x", std::stoi )
CPL_DEF_CORE_TAG_INOUT_POINT_X( S32x, "S32x", std::stoi )
CPL_DEF_CORE_TAG_INOUT_POINT_X( U32x, "U32x", std::stoi )
CPL_DEF_CORE_TAG_INOUT_POINT_X( S64x, "S64x", std::stoi )
CPL_DEF_CORE_TAG_INOUT_POINT_X( U64x, "U64x", std::stoi )
CPL_DEF_CORE_TAG_INOUT_POINT_X( float, "Float", std::stof )
CPL_DEF_CORE_TAG_INOUT_POINT_X( double, "Double", std::stod )


} // namespace CPL
