//=-- String.h - Play with String objects... -----------------------*- C++ -*-=
//
//  This class implements a bunch of useful and fast little String manipulation
//    routines.
//
//=---------------------------------------------------------------------------=
//  This file is copyright (c) 1997-2000 Chris Lattner
//=---------------------------------------------------------------------------=

#ifndef STRING_H
#define STRING_H

#include <assert.h>
#include "PlatformAbstraction.h"

#include "Object.h"

class Serialize;
class String;

#ifndef WINDOWS
extern "C" { void free(void*); }  // Forward decl
#else
extern "C" { void __cdecl free(void*); }  // Forward decl
#endif


inline String operator+(const String &s1, const String &s2);

class MSAPI String : public Object {
public :
  // Different options for creating Strings...
  String();                  // Create empty String
  String(const char);        // Create String, put the char into it.
  String(const char *);      // Create String, set equal to X
  String(const String &);    // Copy Constructor

  // IntToStr - Create String, put the int (in ascii) in it
  static String IntToStr(const int I);

  // DoubleToStr - Create String, put the double (in ascii) in it with Prec 
  // digits after decimal
  static String DoubleToStr(const double D, int Prec=6);

  inline ~String() {
    if (--Chunk->RefCount == 0) 
      free(Chunk);
  }

  static unsigned int Hash(const String &S, int TableSize);

  // The next operator does automatic type conversion to char*'s...
  inline operator const char *() const { 
    return Chunk->Data;
  }

  // [] Access method.  Returns the chars from the internal array.  This could
  //   do range checking too.  The first one can only be used on the right
  //   side of an equation, but can be used with const strings.  The second
  //   one can be used on either side of the equation.
  inline char operator[](const int index) const { 
    assert(index <= Chunk->AllocLength && index >= 0 && "Out of bounds access");
    return Chunk->Data[index]; 
  }
  inline char &operator[](const int index) { 
    assert(index <= Chunk->AllocLength && index >= 0 && "Out of bounds access");
    mutate(); 
    return Chunk->Data[index]; 
  }

  inline void operator +=(const String &s) { cat(s); }
  inline String operator+(const char c) const { 
    String Result(*this); Result.cat(String(c)); return Result; 
  }
 
  // Length - Return the length of the String
  inline int Length() const { return Chunk->length; }

  // Fill - Fills the String with "Length" chars...
  void Fill(char c, int Length);

  // cat - Append a String to this String, optionally specifying the maximum
  //   number o chars to copy out of the old String.
  void cat(const String &, const int = -1);

  // ncat - Append a String to this String.  Only append up to Num characters.
  inline void ncat(const String &S, const int Num) { cat(S, Num); }
  inline void strncat(const String &S, const int Num) { cat(S, Num); }

  // cpy - Copy a String.  Also accessed through the = operator.
  void cpy(const String &);
  inline String &operator= (const String &S) { cpy(S); return *this;}

  // strchr - Search for the occurance of a character in the String.  Return the
  //   index into the array of the result, or -1 if non-existant.
  int strchr(const char c, const int Start = 0) const;

  // strrchr - Search for the occurance of a character in the String, starting
  //   from the end of the string, going to the start.  Return the
  //   index into the array of the result, or -1 if non-existant.
  int strrchr(const char c, const int Start = -1) const;

  int substr(const String &Sub, const int Start = 0) const;

  // ReplaceSubString - Replace all occurrances of "Old" with "New".
  int ReplaceSubString(const String &Old, const String &New);

  // Compare - Compare two strings.  Works the same as strcmp.
  int Compare(const String &) const;
  inline int Compare(const Object &S) const { 
    return Compare((const String&)S); 
  }
 
  int iCompare(const String &) const;

  // nCompare - Only compare the first N characters, like strncmp...
  int nCompare(const String &, int N = -1) const;

  // Print - Prints the String.  Called by the << operator;
  inline ostream &print(ostream &o) const { return o << Chunk->Data; };
  istream &ReadLine(istream &);

  // atoi - Returns the value of a String as an integer.  The parameter
  //   can be used to specify the base of the String.
  int atoi(const int = 10) const;

  // Left - Return the leftmost X characters of the String
  int Left(const int X);

  // Right - Remove all but the rightmost X characters of the String
  int Right(const int X);

  // ToUpper - Convert String to upper case
  void ToUpper();

  // ToLower - Convert String to lower case
  void ToLower();

  int Tokenize(const String &, String &);

  int GetParameter(String &Return);
  int GetParameter(int &Return);

  // Hex2Int - Convert a hex digit (0-9,a-z,A-Z) to it's corresponding 
  // value (0-15)
  inline static int Hex2Int(char c) {
    if ((c <= '9') && (c >= '0'))
      return c - '0';
    else if ((c <= 'z') && (c >= 'a')) 
      return c - 'a' + 10;
    else if ((c <= 'Z') && (c >= 'A')) 
      return c - 'A' + 10;
    return 0;
  }

  Serialize &   SaveState(Serialize &) const;
  Serialize &RestoreState(Serialize &);

#ifndef WINDOWS
protected:          // VC++ gets confused about a protected structure
#endif

  // The actual structure that holds stuff... String data is appended to the end
  // of the malloc'ated memory...
  //
  struct StringChunk {
    int length;       // Length of the String 
    int AllocLength;  // Length
    int RefCount;
    char Data[1];     // The data for the string...
  };

protected:
  StringChunk *Chunk;    // This is never null...

  static StringChunk *GetEmptyStringChunk();

  void ValidateLength(int Len, int Preserve = 1);
  void mutate(int NewLen = -1);
};

inline String operator+(const String &s1, const String &s2) {
  String Return(s1);
  Return.cat(s2);
  return Return;
}

inline istream &operator>> (istream &i, String &S) {
  return S.ReadLine(i);
}

inline Serialize &operator<<(Serialize &I, const String &S) {
  return S.SaveState(I);
}

inline Serialize &operator>>(Serialize &I, String &S) {
  return S.RestoreState(I);
}

class MSAPI CSString : public String {
public :
  CSString(               ) : String( ) { ; }
  CSString(const char *C  ) : String(C) { ; }
  CSString(const String &S) : String(S) { ; }

  static unsigned int Hash(const CSString &S, int TableSize) {
    CSString S2 = S;
    S2.ToUpper();
    return String::Hash(S2, TableSize);
  }
  
  virtual int Compare(const Object &S) const { 
    return iCompare((const String &)S); 
  }

  inline int operator==(const String &O) const { return (Compare(O) == 0); }
  inline int operator!=(const String &O) const { return (Compare(O) != 0); }
  inline int operator< (const String &O) const { return (Compare(O) <  0); }
  inline int operator<=(const String &O) const { return (Compare(O) <= 0); }
  inline int operator> (const String &O) const { return (Compare(O) >  0); }
  inline int operator>=(const String &O) const { return (Compare(O) >= 0); }
};

#endif
