/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Mozilla.
 *
 * The Initial Developer of the Original Code is IBM Corporation.
 * Portions created by IBM Corporation are Copyright (C) 2003
 * IBM Corporation. All Rights Reserved.
 *
 * Contributor(s):
 *   Scott Collins <scc@mozilla.org> (original author)
 *   Darin Fisher <darin@meer.net>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */


  /**
   * The base for string comparators
   */
class NS_COM nsTStringComparator_CharT
  {
    public:
      typedef CharT char_type;

      nsTStringComparator_CharT() {}

      virtual int operator()( const char_type*, const char_type*, PRUint32 length ) const = 0;
      virtual int operator()( char_type, char_type ) const = 0;
  };


  /**
   * The default string comparator (case-sensitive comparision)
   */
class NS_COM nsTDefaultStringComparator_CharT
    : public nsTStringComparator_CharT
  {
    public:
      typedef CharT char_type;

      nsTDefaultStringComparator_CharT() {}

      virtual int operator()( const char_type*, const char_type*, PRUint32 length ) const;
      virtual int operator()( char_type, char_type ) const;
  };


  /**
   * nsTAString is the most abstract class in the string hierarchy.
   *
   * In its original inception, nsTAString was designed to allow the data
   * storage for a string to be separated into multiple fragments.  This was
   * intended to enable lazy string flattening or avoid string flattening
   * altogether in some cases.  This abstraction, however, meant that every
   * single string operation (including simple operations such as IsEmpty() and
   * BeginReading()) required virtual function calls.  A virtual destructor was
   * also required.  This not only meant additional overhead for invoking
   * string methods but also added to additional codesize at every callsite (to
   * load the virtual function address).
   *
   * Today nsTAString exists mainly for backwards compatibility of the string
   * API.  It is restricted to representing a contiguous array of characters,
   * where the character array is not necessarily null-terminated.  Moreover,
   * since nsTAString's virtual function table was frozen for Mozilla 1.0,
   * nsTAString necessarily maintains ABI compatibility with older versions of
   * Gecko.  (nsTObsoleteAString provides that frozen ABI.  See
   * nsObsoleteAString.h for a description of how we solve the ABI
   * compatibility requirement while eliminating virtual function calls on
   * nsTAString.)
   *
   * XPIDL still generates C++ header files with references to nsTAStrings, so
   * nsTAString will still be heavily used in real code.
   *
   * If the opportunity to break ABI compatibility with Mozilla 1.0 were to
   * ever arise, our first move should be to make nsTAString equate to
   * nsTSubstring.  This may in fact be an option today for some Gecko-based
   * products.
   */
class nsTAString_CharT
  {
    public:

      typedef CharT                                  char_type;
      typedef nsCharTraits<char_type>                char_traits;

      typedef char_traits::incompatible_char_type    incompatible_char_type;

      typedef nsTAString_CharT                       self_type;
      typedef nsTAString_CharT                       abstract_string_type;
      typedef nsTObsoleteAString_CharT               obsolete_string_type;
      typedef nsTSubstring_CharT                     substring_type;
      typedef nsTSubstringTuple_CharT                substring_tuple_type;

      typedef nsReadingIterator<char_type>           const_iterator;
      typedef nsWritingIterator<char_type>           iterator;

      typedef nsTStringComparator_CharT              comparator_type;

      typedef PRUint32                               size_type;
      typedef PRUint32                               index_type;

    public:

        // this acts like a virtual destructor
      NS_COM NS_FASTCALL ~nsTAString_CharT();


        /**
         * BeginReading/EndReading can be used to get immutable access to the
         * string's underlying buffer.  EndReading returns a pointer to the
         * end of the string's buffer.  nsReadableUtils.h provides a collection
         * of utility functions that work with these iterators.
         */

      inline const_iterator& BeginReading( const_iterator& iter ) const
        {
          size_type len = GetReadableBuffer(&iter.mStart);
          iter.mEnd = iter.mStart + len;
          iter.mPosition = iter.mStart;
          return iter;
        }

      inline const_iterator& EndReading( const_iterator& iter ) const
        {
          size_type len = GetReadableBuffer(&iter.mStart);
          iter.mEnd = iter.mStart + len;
          iter.mPosition = iter.mEnd;
          return iter;
        }


        /**
         * BeginWriting/EndWriting can be used to get mutable access to the
         * string's underlying buffer.  EndWriting returns a pointer to the
         * end of the string's buffer.  This iterator API cannot be used to
         * grow a buffer.  Use SetLength to resize the string's buffer.
         */

      inline iterator& BeginWriting( iterator& iter )
        {
          size_type len = GetWritableBuffer(&iter.mStart);
          iter.mEnd = iter.mStart + len;
          iter.mPosition = iter.mStart;
          return iter;
        }

      inline iterator& EndWriting( iterator& iter )
        {
          size_type len = GetWritableBuffer(&iter.mStart);
          iter.mEnd = iter.mStart + len;
          iter.mPosition = iter.mEnd;
          return iter;
        }


        /**
         * Length checking functions.  IsEmpty is a helper function to avoid
         * writing code like: |if (str.Length() == 0)|
         */

      NS_COM size_type NS_FASTCALL Length() const;
      PRBool IsEmpty() const { return Length() == 0; }


        /**
         * String equality tests.  Pass a string comparator if you want to
         * control how the strings are compared.  By default, a binary
         * "case-sensitive" comparision is performed.
         */

      NS_COM PRBool NS_FASTCALL Equals( const self_type& ) const;
      NS_COM PRBool NS_FASTCALL Equals( const self_type&, const comparator_type& ) const;
      NS_COM PRBool NS_FASTCALL Equals( const char_type* ) const;
      NS_COM PRBool NS_FASTCALL Equals( const char_type*, const comparator_type& ) const;

        /**
         * An efficient comparison with ASCII that can be used even
         * for wide strings. Call this version when you know the
         * length of 'data'.
         */
      NS_COM PRBool NS_FASTCALL EqualsASCII( const char* data, size_type len ) const;
        /**
         * An efficient comparison with ASCII that can be used even
         * for wide strings. Call this version when 'data' is
         * null-terminated.
         */
      NS_COM PRBool NS_FASTCALL EqualsASCII( const char* data ) const;

    // EqualsLiteral must ONLY be applied to an actual literal string.
    // Do not attempt to use it with a regular char* pointer, or with a char
    // array variable.
    // The template trick to acquire the array length at compile time without
    // using a macro is due to Corey Kosak, with much thanks.
#ifdef NS_DISABLE_LITERAL_TEMPLATE
      inline PRBool EqualsLiteral( const char* str ) const
        {
          return EqualsASCII(str);
        }
#else
      template<int N>
      inline PRBool EqualsLiteral( const char (&str)[N] ) const
        {
          return EqualsASCII(str, N-1);
        }
      template<int N>
      inline PRBool EqualsLiteral( char (&str)[N] ) const
        {
          const char* s = str;
          return EqualsASCII(s, N-1);
        }
#endif

    // The LowerCaseEquals methods compare the lower case version of
    // this string to some ASCII/Literal string. The ASCII string is
    // *not* lowercased for you. If you compare to an ASCII or literal
    // string that contains an uppercase character, it is guaranteed to
    // return false. We will throw assertions too.
      NS_COM PRBool NS_FASTCALL LowerCaseEqualsASCII( const char* data, size_type len ) const;
      NS_COM PRBool NS_FASTCALL LowerCaseEqualsASCII( const char* data ) const;

    // LowerCaseEqualsLiteral must ONLY be applied to an actual
    // literal string.  Do not attempt to use it with a regular char*
    // pointer, or with a char array variable. Use
    // LowerCaseEqualsASCII for them.
#ifdef NS_DISABLE_LITERAL_TEMPLATE
      inline PRBool LowerCaseEqualsLiteral( const char* str ) const
        {
          return LowerCaseEqualsASCII(str);
        }
#else
      template<int N>
      inline PRBool LowerCaseEqualsLiteral( const char (&str)[N] ) const
        {
          return LowerCaseEqualsASCII(str, N-1);
        }
      template<int N>
      inline PRBool LowerCaseEqualsLiteral( char (&str)[N] ) const
        {
          const char* s = str;
          return LowerCaseEqualsASCII(s, N-1);
        }
#endif

        /**
         * A string always references a non-null data pointer.  In some
         * applications (e.g., the DOM) it is necessary for a string class
         * to have some way to distinguish an empty string from a null (or
         * void) string.  These methods enable support for the concept of
         * a void string.
         */

      NS_COM PRBool NS_FASTCALL IsVoid() const;
      NS_COM void NS_FASTCALL SetIsVoid( PRBool );


        /**
         * This method returns true if the string's underlying buffer is
         * null-terminated.  This should rarely be needed by applications.
         * The PromiseFlatTString method should be used to ensure that a
         * string's underlying buffer is null-terminated.
         */

      NS_COM PRBool NS_FASTCALL IsTerminated() const;


        /**
         * These are contant time since nsTAString uses flat storage
         */
      NS_COM char_type NS_FASTCALL First() const;
      NS_COM char_type NS_FASTCALL Last() const;


        /**
         * Returns the number of occurances of the given character.
         */
      NS_COM size_type NS_FASTCALL CountChar( char_type ) const;


        /**
         * Locates the offset of the first occurance of the character.  Pass a
         * non-zero offset to control where the search begins.
         */

      NS_COM PRInt32 NS_FASTCALL FindChar( char_type, index_type offset = 0 ) const;


        /**
         * SetCapacity is not required to do anything; however, it can be used
         * as a hint to the implementation to reduce allocations.
         *
         * SetCapacity(0) is a suggestion to discard all associated storage.
         */
      NS_COM void NS_FASTCALL SetCapacity( size_type );


        /**
         * XXX talk to dbaron about this comment.  we do need a method that
         * XXX allows someone to resize a string's buffer so that it can be
         * XXX populated using writing iterators.  SetLength seems to be the
         * XXX right method for the job, and we do use it in this capacity
         * XXX in certain places.
         *
         * SetLength is used in two ways:
         *   1) to |Cut| a suffix of the string;
         *   2) to prepare to |Append| or move characters around.
         *
         * External callers are not allowed to use |SetLength| in this
         * latter capacity, and should prefer |Truncate| for the former.
         * In other words, |SetLength| is deprecated for all use outside
         * of the string library and the internal use may at some point
         * be replaced as well.
         *
         * This distinction makes me think the two different uses should
         * be split into two distinct functions.
         */
      NS_COM void NS_FASTCALL SetLength( size_type );


        /**
         * Can't use |Truncate| to make a string longer!
         */
      void Truncate( size_type aNewLength=0 )
        {
          NS_ASSERTION(aNewLength <= Length(), "Truncate cannot make string longer");
          SetLength(aNewLength);
        }


        /**
         * |Assign| and |operator=| make |this| equivalent to the string or
         * buffer given as an argument.  If possible, they do this by sharing
         * a reference counted buffer (see |nsTSubstring|).  If not, they copy
         * the buffer into their own buffer.
         */

      NS_COM void NS_FASTCALL Assign( const self_type& readable );
      NS_COM void NS_FASTCALL Assign( const substring_tuple_type& tuple );
      NS_COM void NS_FASTCALL Assign( const char_type* data );
      NS_COM void NS_FASTCALL Assign( const char_type* data, size_type length );
      NS_COM void NS_FASTCALL Assign( char_type c );

      NS_COM void NS_FASTCALL AssignASCII( const char* data, size_type length );
      NS_COM void NS_FASTCALL AssignASCII( const char* data );

    // AssignLiteral must ONLY be applied to an actual literal string.
    // Do not attempt to use it with a regular char* pointer, or with a char
    // array variable. Use AssignASCII for those.
#ifdef NS_DISABLE_LITERAL_TEMPLATE
      void AssignLiteral( const char* str )
                  { AssignASCII(str); }
#else
      template<int N>
      void AssignLiteral( const char (&str)[N] )
                  { AssignASCII(str, N-1); }
      template<int N>
      void AssignLiteral( char (&str)[N] )
                  { AssignASCII(str, N-1); }
#endif

        // copy-assignment operator.  I must define my own if I don't want the compiler to make me one
      self_type& operator=( const self_type& readable )                                             { Assign(readable); return *this; }
      self_type& operator=( const substring_tuple_type& tuple )                                     { Assign(tuple); return *this; }
      self_type& operator=( const char_type* data )                                                 { Assign(data); return *this; }
      self_type& operator=( char_type c )                                                           { Assign(c); return *this; }



        /**
         * |Append|, |operator+=| are used to add characters to the end of this string.
         */ 

      NS_COM void NS_FASTCALL Append( const self_type& readable );
      NS_COM void NS_FASTCALL Append( const substring_tuple_type& tuple );
      NS_COM void NS_FASTCALL Append( const char_type* data );
      NS_COM void NS_FASTCALL Append( const char_type* data, size_type length );
      NS_COM void NS_FASTCALL Append( char_type c );

      NS_COM void NS_FASTCALL AppendASCII( const char* data, size_type length );
      NS_COM void NS_FASTCALL AppendASCII( const char* data );

    // AppendLiteral must ONLY be applied to an actual literal string.
    // Do not attempt to use it with a regular char* pointer, or with a char
    // array variable. Use AppendASCII for those.
#ifdef NS_DISABLE_LITERAL_TEMPLATE
      void AppendLiteral( const char* str )
                  { AppendASCII(str); }
#else
      template<int N>
      void AppendLiteral( const char (&str)[N] )
                  { AppendASCII(str, N-1); }
      template<int N>
      void AppendLiteral( char (&str)[N] )
                  { AppendASCII(str, N-1); }
#endif

      self_type& operator+=( const self_type& readable )                                            { Append(readable); return *this; }
      self_type& operator+=( const substring_tuple_type& tuple )                                    { Append(tuple); return *this; }
      self_type& operator+=( const char_type* data )                                                { Append(data); return *this; }
      self_type& operator+=( char_type c )                                                          { Append(c); return *this; }


        /**
         * |Insert| is used to add characters into this string at a given position.
         * NOTE: It's a shame the |pos| parameter isn't at the front of the arg list.
         */ 

      NS_COM void NS_FASTCALL Insert( const self_type& readable, index_type pos );
      NS_COM void NS_FASTCALL Insert( const substring_tuple_type& tuple, index_type pos );
      NS_COM void NS_FASTCALL Insert( const char_type* data, index_type pos );
      NS_COM void NS_FASTCALL Insert( const char_type* data, index_type pos, size_type length );
      NS_COM void NS_FASTCALL Insert( char_type c, index_type pos );


        /**
         * |Cut| is used to remove a range of characters from this string.
         */

      NS_COM void NS_FASTCALL Cut( index_type cutStart, size_type cutLength );

      
        /**
         * |Replace| is used overwrite a range of characters from this string.
         */

      NS_COM void NS_FASTCALL Replace( index_type cutStart, size_type cutLength, const self_type& readable );
      NS_COM void NS_FASTCALL Replace( index_type cutStart, size_type cutLength, const substring_tuple_type& readable );

      
        /**
         * this is public to support automatic conversion of tuple to abstract
         * string, which is necessary to support our API.
         */
      nsTAString_CharT(const substring_tuple_type& tuple)
        : mVTable(obsolete_string_type::sCanonicalVTable)
        , mData(nsnull)
        , mLength(0)
        , mFlags(0)
        {
          Assign(tuple);
        }

    protected:

      friend class nsTSubstringTuple_CharT;

      // GCC 3.2 erroneously needs these (even though they are subclasses!)
      friend class nsTSubstring_CharT;
      friend class nsTDependentSubstring_CharT;
      friend class nsTPromiseFlatString_CharT;

        /**
         * the address of our virtual function table.  required for backwards
         * compatibility with Mozilla 1.0 frozen nsAC?String interface.
         */
      const void* mVTable;

        /**
         * these fields are "here" only when mVTable == sCanonicalVTable.
         *
         * they exist to support automatic construction of a nsTAString
         * from a nsTSubstringTuple.
         */
      char_type*  mData;
      size_type   mLength;
      PRUint32    mFlags;

        /**
         * nsTAString must be subclassed before it can be instantiated.
         */
      nsTAString_CharT(char_type* data, size_type length, PRUint32 flags)
        : mVTable(obsolete_string_type::sCanonicalVTable)
        , mData(data)
        , mLength(length)
        , mFlags(flags)
        {}

        /**
         * optional ctor for use by subclasses.
         *
         * NOTE: mData and mLength are intentionally left uninitialized.
         */
      explicit
      nsTAString_CharT(PRUint32 flags)
        : mVTable(obsolete_string_type::sCanonicalVTable)
        , mFlags(flags)
        {}

        /**
         * get pointer to internal string buffer (may not be null terminated).
         * return length of buffer.
         */
      NS_COM size_type NS_FASTCALL GetReadableBuffer( const char_type **data ) const;
      NS_COM size_type NS_FASTCALL GetWritableBuffer(       char_type **data );

        /**
         * returns true if this tuple is dependent on (i.e., overlapping with)
         * the given char sequence.
         */
      PRBool NS_FASTCALL IsDependentOn(const char_type *start, const char_type *end) const;

        /**
         * we can be converted to a const nsTSubstring (dependent on this)
         */
      const substring_type NS_FASTCALL ToSubstring() const;

        /**
         * type cast helpers
         */

      const obsolete_string_type* AsObsoleteString() const
        {
          return NS_REINTERPRET_CAST(const obsolete_string_type*, this);
        }

      obsolete_string_type* AsObsoleteString()
        {
          return NS_REINTERPRET_CAST(obsolete_string_type*, this);
        }

      const substring_type* AsSubstring() const
        {
          return NS_REINTERPRET_CAST(const substring_type*, this);
        }

      substring_type* AsSubstring()
        {
          return NS_REINTERPRET_CAST(substring_type*, this);
        }

    private:

        // GCC 2.95.3, EGCS-2.91.66, Sun Workshop/Forte, and IBM VisualAge C++
        // require a public copy-constructor in order to support automatic
        // construction of a nsTAString from a nsTSubstringTuple.  I believe
        // enabling the default copy-constructor is harmless, but I do not want
        // it to be enabled by default because that might tempt people into
        // using it (where it would be invalid).
#if !defined(__SUNPRO_CC) && \
   !(defined(_AIX) && defined(__IBMCPP__)) && \
   (!defined(__GNUC__) || __GNUC__ > 2 || __GNUC_MINOR__ > 95)

        // NOT TO BE IMPLEMENTED
      nsTAString_CharT( const self_type& );

#endif

        // NOT TO BE IMPLEMENTED
      void operator=     ( incompatible_char_type );
      void Assign        ( incompatible_char_type );
      void operator+=    ( incompatible_char_type );
      void Append        ( incompatible_char_type );
      void Insert        ( incompatible_char_type, index_type );
  };


NS_COM
int NS_FASTCALL Compare( const nsTAString_CharT& lhs, const nsTAString_CharT& rhs, const nsTStringComparator_CharT& = nsTDefaultStringComparator_CharT() );


inline
PRBool operator!=( const nsTAString_CharT& lhs, const nsTAString_CharT& rhs )
  {
    return !lhs.Equals(rhs);
  }

inline
PRBool operator< ( const nsTAString_CharT& lhs, const nsTAString_CharT& rhs )
  {
    return Compare(lhs, rhs)< 0;
  }

inline
PRBool operator<=( const nsTAString_CharT& lhs, const nsTAString_CharT& rhs )
  {
    return Compare(lhs, rhs)<=0;
  }

inline
PRBool operator==( const nsTAString_CharT& lhs, const nsTAString_CharT& rhs )
  {
    return lhs.Equals(rhs);
  }

inline
PRBool operator>=( const nsTAString_CharT& lhs, const nsTAString_CharT& rhs )
  {
    return Compare(lhs, rhs)>=0;
  }

inline
PRBool operator> ( const nsTAString_CharT& lhs, const nsTAString_CharT& rhs )
  {
    return Compare(lhs, rhs)> 0;
  }
