#include <iostream>
#include <iomanip>
#include <cstring> // strcpy and strcat prototypes
#include <cstdlib> // exit prototype
using namespace std;

class String
{
    friend ostream &operator<<(ostream &, const String &);
    friend istream &operator>>(istream &, String &);
public:
    String(const char * = ""); // conversion/default constructor
    String(const String &); // copy constructor
    ~String(); // destructor

    const String &operator=(const String &); // assignment operator
    const String &operator+=(const String &); // concatenation operator

    bool operator!() const; // is String empty?
    bool operator==(const String &) const; // test s1 == s2
    bool operator<(const String &) const; // test s1 < s2
    bool operator!=(const String &right) const;   // test s1 != s2
    bool operator>(const String &right) const; // test s1 > s2
    bool operator<=(const String &right) const;// test s1 <= s2
    bool operator>=(const String &right) const; // test s1 >= s2

    char &operator[](int); // subscript operator (modifiable lvalue)
    int getLength() const; // return string length
private:
    int length; // string length (not counting null terminator)
    char *sPtr; // pointer to start of pointer-based string

    void setString(const char *); // utility function
}; // end class String

ostream &operator<<(ostream &os, const String & str)
{
    if(str.sPtr) os << str.sPtr;
    return os;
}

istream &operator>>(istream & is, String & str)
{
    char temp[200]; temp[0] = 0; is >> temp;
    str.setString(temp);
    return is;
}

String::String(const char * s)
{
    sPtr = NULL;
    this->setString(s);
    if (s) cout << "Conversion (and default) constructor: "<< s << endl;
}

String::String(const String& a)
{
    sPtr = NULL;
    if (a.length) this->setString(a.sPtr);
    if (a.length) cout << "Copy constructor: " << a.sPtr << endl;
}

String::~String()
{
    if (sPtr){
        cout << "Destructor: " << sPtr << endl;
        delete[] sPtr;
    }
    sPtr = NULL;
}

char & String::operator[](int i)
{
    if (i < length)
        return sPtr[i];
    return sPtr[length];
}

int String::getLength() const
{
    return length;
}

void String::setString(const char * s)
{
    length = 0;
    if (sPtr) delete[] sPtr;
    sPtr = NULL;

    if (s && s[0]){
        delete[] sPtr;
        length = strlen(s);
        sPtr = new char[length + 1];
        strcpy(sPtr, s);
    }
}

bool String::operator!() const
{
    return length == 0;
}

bool String::operator<(const String &right) const
{
    return strcmp(sPtr, right.sPtr) < 0;
}

bool String::operator>(const String &right) const
{
    return strcmp(sPtr, right.sPtr) > 0;
}

bool String::operator>=(const String &right) const
{
    return strcmp(sPtr, right.sPtr) >= 0;
}

bool String::operator<=(const String &right) const
{
    return strcmp(sPtr, right.sPtr) <= 0;
}

bool String::operator!=(const String &right) const
{
    return strcmp(sPtr, right.sPtr) != 0;
}

bool String::operator==(const String &right) const
{
    return strcmp(sPtr, right.sPtr) == 0;
}

const String & String::operator+=(const String & s)
{
    char* tmp = new char[s.length + length + 1];
    strcpy(tmp, sPtr);
    strcpy(tmp + length, s.sPtr);
    if (sPtr) delete[]sPtr;
    sPtr = tmp;
    length = s.length + length;
    return *this;
}

const String & String::operator=(const String & s)
{
    cout << "operator= called" << endl;
    this->setString(s.sPtr);
    return *this;
}

int main()
{
    String s1("happy");
    String s2(" birthday");
    String s3;

    // test overloaded equality and relational operators
    cout << "s1 is \"" << s1 << "\"; s2 is \"" << s2
        << "\"; s3 is \"" << s3 << '\"'
        << boolalpha << "\n\nThe results of comparing s2 and s1:"
        << "\ns2 == s1 yields " << (s2 == s1)
        << "\ns2 != s1 yields " << (s2 != s1)
        << "\ns2 >  s1 yields " << (s2 > s1)
        << "\ns2 <  s1 yields " << (s2 < s1)
        << "\ns2 >= s1 yields " << (s2 >= s1)
        << "\ns2 <= s1 yields " << (s2 <= s1);


    // test overloaded String empty (!) operator
    cout << "\n\nTesting !s3:" << endl;

    if (!s3)
    {
        cout << "s3 is empty; assigning s1 to s3;" << endl;
        s3 = s1; // test overloaded assignment
        cout << "s3 is \"" << s3 << "\"";
    } // end if

    // test overloaded String concatenation operator
    cout << "\n\ns1 += s2 yields s1 = ";
    s1 += s2; // test overloaded concatenation
    cout << s1;

    // test conversion constructor
    cout << "\n\ns1 += \" to you\" yields" << endl;
    s1 += " to you"; // test conversion constructor
    cout << "s1 = " << s1 << "\n";

    // test copy constructor
    String *s4Ptr = new String(s1);
    cout << "\n*s4Ptr = " << *s4Ptr << "\n";

    // test destructor
    delete s4Ptr;

    // test using subscript operator to create a modifiable lvalue
    s1[0] = 'H';
    s1[6] = 'B';
    cout << "\ns1 after s1[0] = 'H' and s1[6] = 'B' is: "
        << s1 << "\n";

    // test subscript out of range
    cout << "Attempt to assign 'd' to s1[30] yields:" << endl;
    s1[30] = 'd'; // ERROR: subscript out of range

    return 0;
} // end main