/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2016/06/06 - 2016/06/06
/// 
/// @brief  タグ関連
/// 
/// ****************************************************************************

#include "../Base/CPL_Core_LexicalAnalysis.h"
#include "CPL_Core_Tag.h"

namespace CPL {
namespace Core {

namespace zzz {

// _____________________________________________________________________________
// 始端スペース削除。
void eraseBeforeSpace_( rio<std::string> io_str )
{
  // 始端スペースカウント。
  auto itr_search = io_str.begin();
  while( itr_search != io_str.end() )
  {
    if( *itr_search != ' ' )
    {
      break;
    }
    ++itr_search;
  }

  //
  io_str.erase( io_str.begin(), itr_search );
}

// _____________________________________________________________________________
// 終端スペース削除。
void eraseAfterSpace_( rio<std::string> io_str )
{
  // 終端削除。
  while( !io_str.empty() )
  {
    if( io_str.back() == ' ' )
    {
      io_str.pop_back();
      continue;
    }
    break;
  }
}

// _____________________________________________________________________________
void eraseSideSpace_( rio<std::string> io_str )
{
  // 最初に終端スペース削除。
  eraseAfterSpace_( io_str );

  // 始端スペース削除。
  eraseBeforeSpace_( io_str );
}


// _____________________________________________________________________________
sdptr<Tag> Tag::toData_( rvref<Tag> tag )
{
  return Tag::Create( *this, std::move(tag) );
}

// _____________________________________________________________________________
sdptr<Tag> Tag::toData_( cref<Tag> tag )
{
  return Tag::Create( *this, tag );
}

// _____________________________________________________________________________
sdptr<Tag> Tag::toData_( pin<char> str )
{
  if( str[0] != '"'  &&  strchr(str, ' ') ) // 囲み文字列でなく、スペースが含まれる場合。
  {
    return AddTag( Tag{ std::string("\"") + str + "\"" } );
  }
  else
  {
    return AddTag( Tag{ str } );
  }
}

// _____________________________________________________________________________
sdptr<Tag> Tag::toData_( rvref<std::string> str )
{
  if( str.front() != '"'  &&  str.find( ' ', 0 ) != std::string::npos )
  {
    return AddTag( Tag{ std::string("\"") + str + "\"" } );
  }
  else
  {
    return AddTag( Tag{ str } );
  }
}

// _____________________________________________________________________________
sdptr<Tag> Tag::toData_( cref<std::string> str )
{
  if( str.front() != '"'  &&  str.find( ' ', 0 ) != std::string::npos )
  {
    return AddTag( Tag{ std::string("\"") + str + "\"" } );
  }
  else
  {
    return AddTag( Tag{ str } );
  }
}

//// _____________________________________________________________________________
//sdptr<Tag> Tag::toData_( int value )
//{
//  return AddTag( Tag{ std::to_string( value ) } );
//}
//// _____________________________________________________________________________
//sdptr<Tag> Tag::toData_( U32 value )
//{
//  return AddTag( Tag{ std::to_string( value ) } );
//}
//// _____________________________________________________________________________
//sdptr<Tag> Tag::toData_( S64 value )
//{
//  return AddTag( Tag{ std::to_string( value ) } );
//}
//// _____________________________________________________________________________
//sdptr<Tag> Tag::toData_( U64 value )
//{
//  return AddTag( Tag{ std::to_string( value ) } );
//}
//
//// _____________________________________________________________________________
//sdptr<Tag> Tag::toData_( float value )
//{
//  return AddTag( Tag{ std::to_string( value ) } );
//}
//// _____________________________________________________________________________
//sdptr<Tag> Tag::toData_( double value )
//{
//  return AddTag( Tag{ std::to_string( value ) } );
//}

// _____________________________________________________________________________
sdptr<Tag> Tag::toData_( bool value )
{
  return AddTag( Tag{ value ? "true" : "false" } );
}

// _____________________________________________________________________________
std::string Tag::StrIndent_( int indent )
{
  std::string s;
  for( int i = 0; i < indent; ++i )
  {
    s += "\t";
  }
  return s;
}

// _____________________________________________________________________________
std::string Tag::Output_( bool is_root, int in_indent ) const
{
  std::string s{ StrIndent_( in_indent ) };

  if( this->GetLinkChild() == nullptr )
  {
    return s + m_name;
  }
  else // データがある場合はタグとして扱う。
  {
    bool is_grandchild = false;
    //
    auto sp_child = GetLinkChild();
    for( auto& datum : *GetLinkChild() )
    {
      if( datum.GetLinkChild() != nullptr )
      {
        is_grandchild = true;
        break;
      }
    }

    if( !m_name.empty() )
    {
      s += m_name + "{";
    }
    if( is_grandchild )
    {
      s += "\n";
    }
    else
    {
      s += " ";
    }
    // 子供ループ
    auto sp_child_end = GetLinkChild()->GetLinkEnd();
    for( auto& datum : *GetLinkChild() )
    {
      s += datum.Output_( false, is_grandchild ? in_indent + 1 : 0 );

      // データ最後尾チェック。
      if( &datum != sp_child_end.get() )
      {
        s += ",";
      }
      if( is_grandchild )
      {
        s += "\n";
      }
    }
    if( !m_name.empty() )
    {
      if( is_grandchild )
      {
        s += StrIndent_(in_indent) + "}";
      }
      else
      {
        s += " }";
      }
    }
    if( is_root )
    {
      s += "\n";
    }
    return s;
  }
  return "";
}


// _____________________________________________________________________________
/// @brief タグブロック解析
/// 
/// @param[in] io_tag_parent
///   : 親タグを指定します。
/// @param[in] in_source
///   : 解析する文字列の先頭アドレスを指定します。
/// @param[in] in_size
///   : in_sourceで渡されるバッファのサイズを指定します。\n
///   : -1を指定するとnull終端として処理します。
/// 
/// @retval true  : 解析成功。
/// @retval false : 解析失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  Tag::Input
    ( rout<Tag  > io_tag_parent
    , pin<char  > in_source
    , _in<size_t> in_size
    )
{
  Core::Tokenizer tknz( in_source, in_size, Core::eCharCode::UTF8 );
  tknz.InitTokenKey_for_C();
  return Input( io_tag_parent, tknz );
}


// _____________________________________________________________________________
/// @brief タグブロック解析
/// 
/// @param[in] io_tag_parent
///   : 親タグを指定します。
/// @param[in] io_tokenizer
///   : 解析する構文解析オブジェクトを指定します。
/// 
/// @retval true  : 解析成功。
/// @retval false : 解析失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Tag::Input( rio<Tag> io_tag_parent, rio<Core::Tokenizer> io_tokenizer )
{
  //sm_tag_active() = GetTagActive()->AddTag( GetLinkMyself() );
  io_tag_parent.AddTag( GetLinkMyself() );

  std::string str_data;
  Core::Token tkn;
  while( io_tokenizer.GetNextToken( tkn ) )
  {
    switch( tkn.key )
    {
    case Core::Token::eKey::CommentError:
    case Core::Token::eKey::Error:
    case Core::Token::eKey::StringError:
    case Core::Token::eKey::Null:
      return false;
    case Core::Token::eKey::NewLine:
    case Core::Token::eKey::Comment:
      break;

    case '{':
      if( auto sp_new_tag = toData_(str_data) )
      {
        if( !sp_new_tag->Input( *GetLinkMyself(), io_tokenizer ) )
        {
          return false;
        }
        str_data.clear();
      }
      break;

    case ';':
    case ',':
      // 両端のスペースを消す。
      eraseSideSpace_( str_data );
      //
      if( !str_data.empty() )
      {
        toData_( str_data );
        str_data.clear();
        ++m_num_child;
      }
      break;

    case '}':
      // 両端のスペースを消す。
      eraseSideSpace_( str_data );
      //
      if( !str_data.empty() )
      {
        toData_( str_data );
        ++m_num_child;
      }
      //
      //sm_tag_active() = GetLinkParent();
      //if( sm_tag_active().expired() )
      //{
      //  return true;
      //}
      return true;

    case Core::Token::eKey::String:
      str_data = str_data + "\"" + tkn.str + "\"";
      break;

    case Core::Token::eKey::Space:
      if( !str_data.empty() )
      {
        str_data += tkn.str;
      }
      break;

    default:
      str_data += tkn.str;
      break;
    }
  }
  return false;
}


// _____________________________________________________________________________
/// @brief タグ解析
/// 
/// @param[in] in_source
///   : 解析する文字列の先頭アドレスを指定します。
/// @param[in] in_size
///   : in_sourceで渡されるバッファのサイズを指定します。\n
///   : -1を指定するとnull終端として処理します。
/// 
/// @return 
/// タグオブジェクト保持用の配列を指定します。\n
/// この配列に設定されるのは、ルート階層のタグです。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
std::vector<sdptr<Tag>>
  Tag::Load
    ( pin<char  > in_source
    , _in<size_t> in_size
    )
{
  Tag tag_root;
  std::vector<sdptr<Tag>> tag_list;
  Core::Tokenizer
    tknz
    { in_source
    , in_size
    , Core::eCharCode::SJIS
    };
  tknz.InitTokenKey_for_C();

  sdptr<Tag> sp_tag;
  Core::Token tkn;
  while( tknz.GetNextToken( tkn ) )
  {
    switch( tkn.key )
    {
    case Core::Token::eKey::String:
    case Core::Token::eKey::Word:
      {
        sp_tag = Tag::Create( tag_root, tkn.str );
      }
      break;

    case '{':
      if( sp_tag  &&  !sp_tag->Input( *sp_tag, tknz ) )
      {
        return tag_list;
      }
      break;
    }
  }

  for
    ( auto sp_tag = tag_root.GetTagChild()
    ; sp_tag
    ; sp_tag = sp_tag->GetTagNext()
    )
  {
    tag_list.push_back( sp_tag );
  }

  return tag_list;
}


} // namespace zzz

} // namespace Core


std::string& operator << ( rout<std::string> s, _in<TBTag> in_tag )
{
  if( auto sp_item = in_tag.GetTagChild() )
  {
    auto s_ = sp_item->GetName();
    if( s_.front() == '"'  &&  s_.back() == '"' ) // 両端に"がある場合は削除する
    {
      s_.erase( s_.end()-1 );
      s_.erase( s_.begin() );
    }
    s = s_;
  }
  return s;
}



} // namespace CPL
