/***************************************************************************
                          String_impl.h  -  description
                             -------------------
    begin                : 2004/04/10 16:36
    copyright            : (C) 2004 by Tomas Oberhuber
    email                : tomas.oberhuber@fjfi.cvut.cz
 ***************************************************************************/

/* See Copyright Notice in tnl/Copyright */

#pragma once

#include <TNL/String.h>
#include <TNL/Assert.h>
#include <TNL/Math.h>
#ifdef HAVE_MPI
   #include <mpi.h>
#endif

namespace TNL {

inline int String::getLength() const
{
   return getSize();
}

inline int String::getSize() const
{
   return this->size();
}

inline int String::getAllocatedSize() const
{
   return this->capacity();
}

inline void String::setSize( int size )
{
   TNL_ASSERT_GE( size, 0, "string size must be non-negative" );
   this->reserve( size );
}

inline const char* String::getString() const
{
   return this->c_str();
}

inline const char& String::operator[]( int i ) const
{
   TNL_ASSERT( i >= 0 && i < getLength(),
               std::cerr << "Accessing char outside the string." );
   return std::string::operator[]( i );
}

inline char& String::operator[]( int i )
{
   TNL_ASSERT( i >= 0 && i < getLength(),
               std::cerr << "Accessing char outside the string." );
   return std::string::operator[]( i );
}

/****
 * Operators for single characters
 */
inline String& String::operator+=( char str )
{
   std::string::operator+=( str );
   return *this;
}

inline String String::operator+( char str ) const
{
   return String( *this ) += str;
}

inline bool String::operator==( char str ) const
{
   return std::string( *this ) == std::string( 1, str );
}

inline bool String::operator!=( char str ) const
{
   return ! operator==( str );
}

/****
 * Operators for C strings
 */
inline String& String::operator+=( const char* str )
{
   std::string::operator+=( str );
   return *this;
}

inline String String::operator+( const char* str ) const
{
   return String( *this ) += str;
}

inline bool String::operator==( const char* str ) const
{
   return std::string( *this ) == str;
}

inline bool String::operator!=( const char* str ) const
{
   return ! operator==( str );
}

/****
 * Operators for std::string
 */
inline String& String::operator+=( const std::string& str )
{
   std::string::operator+=( str );
   return *this;
}

inline String String::operator+( const std::string& str ) const
{
   return String( *this ) += str;
}

inline bool String::operator==( const std::string& str ) const
{
   return std::string( *this ) == str;
}

inline bool String::operator!=( const std::string& str ) const
{
   return ! operator==( str );
}

/****
 * Operators for String
 */
inline String& String::operator+=( const String& str )
{
   std::string::operator+=( str );
   return *this;
}

inline String String::operator+( const String& str ) const
{
   return String( *this ) += str;
}

inline bool String::operator==( const String& str ) const
{
   return std::string( *this ) == str;
}

inline bool String::operator!=( const String& str ) const
{
   return ! operator==( str );
}


inline String::operator bool () const
{
   return getLength();
}

inline bool String::operator!() const
{
   return ! operator bool();
}

inline String
String::replace( const String& pattern,
                 const String& replaceWith,
                 int count ) const
{
   std::string newString = *this;

   std::size_t index = 0;
   for( int i = 0; i < count || count == 0; i++ ) {
      // locate the substring to replace
      index = newString.find( pattern, index );
      if( index == std::string::npos )
         break;

      // make the replacement
      newString.replace( index, pattern.getLength(), replaceWith );
      index += replaceWith.getLength();
   }

   return newString;
}

inline String
String::strip( char strip ) const
{
   int prefix_cut_off = 0;
   int sufix_cut_off = 0;

   while( prefix_cut_off < getLength() && (*this)[ prefix_cut_off ] == strip )
      prefix_cut_off++;

   while( sufix_cut_off < getLength() && (*this)[ getLength() - 1 - sufix_cut_off ] == strip )
      sufix_cut_off++;

   if( prefix_cut_off + sufix_cut_off < getLength() )
      return substr( prefix_cut_off, getLength() - prefix_cut_off - sufix_cut_off );
   return "";
}

inline std::vector< String >
String::split( const char separator, SplitSkip skip ) const
{
   std::vector< String > parts;
   String s;
   for( int i = 0; i < this->getLength(); i++ ) {
      if( ( *this )[ i ] == separator ) {
         if( skip != SplitSkip::SkipEmpty || s != "" )
            parts.push_back( s );
         s = "";
      }
      else s += ( *this )[ i ];
   }
   if( skip != SplitSkip::SkipEmpty || s != "" )
      parts.push_back( s );
   return parts;
}

inline bool
String::startsWith( const String& prefix ) const
{
   if( prefix.getSize() > getSize())
      return false;
   return std::equal( prefix.begin(), prefix.end(), begin() );
}

inline bool
String::endsWith( const String& suffix ) const
{
   if( suffix.getSize() > getSize())
      return false;
   return std::equal( suffix.rbegin(), suffix.rend(), rbegin() );
}

inline String operator+( char string1, const String& string2 )
{
   return convertToString( string1 ) + string2;
}

inline String operator+( const char* string1, const String& string2 )
{
   return String( string1 ) + string2;
}

inline String operator+( const std::string& string1, const String& string2 )
{
   return String( string1 ) + string2;
}

#ifdef HAVE_MPI
inline void mpiSend( const String& str, int target, int tag, MPI_Comm mpi_comm )
{
   int size = str.getSize();
   MPI_Send( &size, 1, MPI_INT, target, tag, mpi_comm );
   MPI_Send( const_cast< void* >( ( const void* ) str.getString() ), str.length(), MPI_CHAR, target, tag, mpi_comm );
}

inline void mpiReceive( String& str, int source, int tag, MPI_Comm mpi_comm )
{
   int size;
   MPI_Status status;
   MPI_Recv( &size, 1, MPI_INT, source, tag, mpi_comm, &status );
   str.setSize( size );
   MPI_Recv( const_cast< void* >( ( const void* ) str.data() ), size, MPI_CHAR, source, tag, mpi_comm,  &status );
}

/*
inline void String :: MPIBcast( int root, MPI_Comm comm )
{
   int iproc;
   MPI_Comm_rank( MPI_COMM_WORLD, &iproc );
   TNL_ASSERT( string, );
   int len = strlen( string );
   MPI_Bcast( &len, 1, MPI_INT, root, comm );
   if( iproc != root )
   {
      if( length < len )
      {
         delete[] string;
         length = STRING_PAGE * ( len / STRING_PAGE + 1 );
         string = new char[ length ];
      }
   }
 
   MPI_Bcast( string, len + 1, MPI_CHAR, root, comm );
}
*/
#endif


} // namespace TNL
