//****************************************************************
//
// apstring class implementation.
// See apstring.h for complete documentation of functions
//
//****************************************************************

#include <string.h>
#include <assert.h>
#include "apstring.h"

const int npos = -1;
const int MAX_LENGTH = 1024;   // Maximum size of input string

//****************************************************************
//********           Constructors / destructor         ***********
//****************************************************************

apstring::apstring()

// Constructor: builds an empty string.

{
    mLength = 0;    
    mCapacity = 1;
    mCstring = new char[mCapacity];
    mCstring[0] = '\0';  // String consists only of the null terminator.
}

//****************************************************************

apstring::apstring(const char *s)

// Constructor: builds a string from a null-terminated string.

{
    assert (s != 0);
    mLength = strlen(s);
    mCapacity = mLength + 1;     // +1 for the terminating null
    mCstring = new char[mCapacity];  
    strcpy(mCstring, s);
}   

//****************************************************************

apstring::apstring(const apstring &str)

// Copy constructor: builds a string equal to a given string str.

{
    mLength = str.length();
    mCapacity = mLength + 1;
    mCstring = new char[mCapacity];
    strcpy(mCstring, str.mCstring);
}

//****************************************************************

apstring::~apstring()

// Destructor.

{
    delete[] mCstring;
}

//****************************************************************
//********                 Assignment                  ***********
//****************************************************************

const apstring &apstring::operator= (const apstring &str)

// Assignment operator from another apstring.

{
    if (this != &str) {

        // If not enough room, reallocate the buffer:
        if (mCapacity < str.length() + 1) {
            delete[] mCstring;
            mCapacity = str.length() + 1;
            mCstring = new char[mCapacity];
        }

        // Copy the string:
        mLength = str.length();
        strcpy(mCstring, str.mCstring);
    }
    return *this;   
}

//****************************************************************

const apstring& apstring::operator= (const char *s)

// Assignment operator from a null-terminated string.

{
    assert(s != 0);
    int len = strlen(s);
    
    // If not enough room, reallocate the buffer:
    if (mCapacity < len + 1) {
        delete[] mCstring;
        mCapacity = len + 1;
        mCstring = new char[mCapacity];       
    }
    mLength = len;
    strcpy(mCstring, s);

    return *this;
}

//****************************************************************

const apstring& apstring::operator= (char ch)

// Assignment operator from a char.

{
    // If not enough room, reallocate the buffer:
    if (mCapacity < 2) {
        delete [] mCstring;
        mCapacity = 2;
        mCstring = new char[mCapacity];
    }
    mLength = 1;
    mCstring[0] = ch;
    mCstring[1] = '\0';  // Terminating null.
    return *this;
}

//****************************************************************
//********                  Subscripting               ***********
//****************************************************************

char apstring::operator[](int k) const

// Overloaded subscript operator.  Verifies that 0 <= k <= mLength - 1.
// Returns a character (to be used as an rvalue).

{
    if (k < 0 || k >= mLength) {
        cerr << "index out of range: " << k << " string: "
             << mCstring << endl;
        assert (0 <= k && k < mLength);
    }
    return mCstring[k];
}

//****************************************************************

char &apstring::operator[] (int k)

// Overloaded subscript operator.  Verifies that 0 <= k <= mLength - 1.
// Returns a reference to a char (to be used as an lvalue).

{
    if (k < 0 || k >= mLength) {
        cerr << "index out of range: " << k << " string: "
             << mCstring << endl;
        assert(0 <= k && k < mLength);
    }
    return mCstring[k];
}     

//****************************************************************
//********              Append operators +=            ***********
//****************************************************************

const apstring &apstring::operator+= (const apstring &str)

// Concatenates a copy of str to this string

{
    // Make a copy of str to avoid aliasing problems:
    apstring copyStr = str;
    
    int newLength = mLength + str.length();
    int lastLocation = mLength;
    
    // If not enough room, reallocate the buffer:
    if (newLength >= mCapacity) {
        mCapacity = newLength + 1;
        char *newBuffer = new char[mCapacity];
        strcpy(newBuffer, mCstring);
        delete [] mCstring;
        mCstring = newBuffer;
    }
    
    // Concatenate copyStr to the end of mCstring
    //   (pointer arithmetic in action):
    strcpy(mCstring + lastLocation, copyStr.c_str());
    mLength = newLength;

    return *this;
}

//****************************************************************

const apstring &apstring::operator+= (char ch)

// Concatenates a char to this string.
//   (short, but inefficient).

{
    apstring temp;

    temp = ch;
    *this += temp;
    return *this;
}

//****************************************************************
//**********           Other member functions         ************
//****************************************************************

int apstring::length() const

// Returns the length of the string.

{
    return mLength;
}

//****************************************************************

int apstring::find(const apstring &str) const

// Finds the first occurrence of the substring str in this string.
// Returns the index of the first character, or npos, if not found.

{
    int len = str.length();
    int lastIndex = mLength - len;
    int k;

    for (k = 0;   k <= lastIndex;   k++)
        if (strncmp(mCstring + k, str.c_str(), len) == 0)
            return k;

    return npos;
}

//****************************************************************

int apstring::find(char ch) const

// Finds the first occurrence of the char ch this string.
// Returns its index, or npos, if not found.

{
    int k;

    for (k = 0;   k < mLength;   k++)
        if (mCstring[k] == ch)
            return k;

    return npos;
}

//****************************************************************

apstring apstring::substr(int pos, int len) const

// Builds and returns the substring of length
//   len starting from pos.

{
    if (pos < 0)
        pos = 0;
    
    if (pos >= mLength)
        return "";             // Return empty string
    
    int lastIndex = pos + len - 1;
    if (lastIndex >= mLength)
        lastIndex = mLength-1;
    
    // Allocate as much space as in this string:
    apstring result(*this);
       // (This is a kluge.  The class does not have a
       //   constructor that would allocate a string buffer
       //   of the specified capacity, and there is no resize function.)

    int j, k;
    for (j = 0, k = pos;   k <= lastIndex;   j++, k++)
        result.mCstring[j] = mCstring[k];

    result.mCstring[j] = '\0';     // Append the terminatig null.
    result.mLength = j;

    return result;
}

//****************************************************************

const char *apstring::c_str() const

// Returns a pointer to the null-terminated string in storage
//   buffer for this apstring.

{
    return mCstring;    
}

//****************************************************************
//****************************************************************
//********   Free-standing functions and operators   *************
//****************************************************************
//****************************************************************

//****************************************************************
//********             I/O functions                 *************
//****************************************************************

ostream &operator<< (ostream &os, const apstring &str)

// Inserts the string into os and returns os.

{
    return os << str.c_str();
}

//****************************************************************

istream &operator>> (istream &is, apstring &str)

// Skips all white space and reads one word from is and returns is.

{
    char buf[MAX_LENGTH];

    is >> buf;
    str = buf;
    return is;
}

//****************************************************************

istream &getline(istream &is, apstring &str)

// Reads a line from input stream is into the string str
{
    char buf[MAX_LENGTH];

    is.getline(buf, MAX_LENGTH);
    str = buf;
    return is;
}

//****************************************************************
//********          Concatenation operators +        *************
//****************************************************************

apstring operator+ (const apstring &str1, const apstring &str2)

// Builds and returns a new string that is concatenation of str1 with str2     
//   (short, but very inefficient).

{
    apstring result = str1;
    result += str2;
    return result;
}

//****************************************************************

apstring operator+ (char ch, const apstring &str)

// Builds and returns a new string that is concatenation of ch with str     

{
    apstring result;

    result = ch;
    result += str;
    return result;
}

//****************************************************************

apstring operator+ (const apstring &str, char ch)

// Builds and returns a new string that is concatenation of str and ch    

{
    apstring result = str;
    result += ch;
    return result;
}

//****************************************************************
//********          Relational operators             *************
//****************************************************************

bool operator== (const apstring &str1, const apstring &str2)
{
    return strcmp(str1.c_str(), str2.c_str()) == 0;
}

//****************************************************************

bool operator!= (const apstring &str1, const apstring &str2)
{
    return !(str1 == str2);
}

//****************************************************************

bool operator< (const apstring &str1, const apstring &str2)
{
    return strcmp(str1.c_str(), str2.c_str()) < 0;
}

//****************************************************************

bool operator<= (const apstring &str1, const apstring &str2)
{
    return strcmp(str1.c_str(), str2.c_str()) <= 0;
}

//****************************************************************

bool operator> (const apstring &str1, const apstring &str2)
{
    return strcmp(str1.c_str(), str2.c_str()) > 0;
}

//****************************************************************

bool operator>= (const apstring &str1, const apstring &str2)
{
    return strcmp(str1.c_str(), str2.c_str()) >= 0;
}

//****************************************************************
