// Implementation of a simple string class.
#include "Str.h"

#include <cstring>
#include <iostream>

using std::cout;
using std::endl;
using std::ostream;

// Print trace messages with object addresses to show when functions get called.
#define TRACE(what)  cout << "- Str::" what << " called on " << this << endl

// constructors

Str::Str() {
  TRACE("default constructor");
  // allocate empty string
  st_ = new char[1];
  st_[0] = '\0';
}

Str::Str(const char* s) {
  TRACE("c-string constructor");
  int len = strlen(s);
  st_ = new char[len + 1];
  strncpy(st_, s, len + 1);
}

Str::Str(const Str& s) {
  TRACE("copy constructor");
  int len = strlen(s.st_);
  st_ = new char[len + 1];
  strncpy(st_, s.st_, len + 1);
}

// destructor
Str::~Str() {
  TRACE("destructor");
  delete [] st_;
}

// operations

int Str::length() const {
  TRACE("length");
  return strlen(st_);
}

char* Str::c_str() const {
  TRACE("c_str");
  int len = strlen(st_);
  char* result = new char[len + 1];
  strncpy(result, st_, len + 1);
  return result;
}


void Str::append(const Str& s) {
  TRACE("append");
  int len1 = strlen(st_);
  int len2 = strlen(s.st_);
  char* newst = new char[len1 + len2 + 1];
  strncpy(newst, st_, len1 + 1);
  strncat(newst, s.st_, len2);
  delete [] st_;
  st_ = newst;
}

Str& Str::operator=(const Str& s) {
  TRACE("assignment");
  // do nothing if trying to assign a string to itself
  if (this == &s) {
    return *this;
  }

  // Replace string array with one of correct size
  // (Note: this is where the self-assignment test _really_ matters)
  // (Note: a better implementation would re-use the existing array
  //  if it's long enough, but not too long)
  delete [] st_;
  int len = strlen(s.st_);
  st_ = new char[len + 1];

  // copy characters and return
  strncpy(st_, s.st_, len + 1);
  return *this;
}

// stream output
// (Note: not a member function of Str.  This is an overloaded global
// function that is a friend of Str so it can access private data)
ostream& operator<<(ostream& out, const Str& s) {
  out << s.st_;
  return out;
}