/*
 * rcString.C - reference counted string class.
 *
 * Copyright (C) 1992, Christoph Streit (streit@iam.unibe.ch)
 *                     Igor Metz (metz@iam.unibe.ch)
 *                     University of Berne, Switzerland
 * All rights reserved.
 *
 * This software may be freely copied, modified, and redistributed
 * provided that this copyright notice is preserved on all copies.
 *
 * You may not distribute this software, in whole or in part, as part of
 * any commercial product without the express consent of the authors.
 *
 * There is no warranty or other guarantee of fitness of this software
 * for any purpose.  It is provided solely "as is".
 *
 */

#ifdef AMIGA_GCC
	#include "string.h"
#endif

#include <iostream.h>
#include "rcString.h"
#include "Error.h"

//___________________________________________________________ rcStringRep
//
// reference counted rcString representation

class rcStringRep 
{
  friend class rcString;

public:
  rcStringRep(char);
  rcStringRep(const char*);
  ~rcStringRep();
  
private:
  char* str;
  int len;
  int count;

private:
  rcStringRep(_Char_p_*);
  void ref();
  void deref();
};

inline rcStringRep::rcStringRep(char c) {
  str = new char[2]; str[0] = c; str[1] = '\0'; 
  count = len = 1;
}

inline rcStringRep::rcStringRep(const char* s) {
  len = strlen(s);
  str = new char[len+1];
  strcpy(str, s);
  count = 1;
}

inline rcStringRep::~rcStringRep() {
  delete [] str; 
}

inline rcStringRep::rcStringRep(_Char_p_* r) { 
  str = *r; count = 1; len = strlen(str); 
}

inline void rcStringRep::ref() {
  count++;
}

inline void rcStringRep::deref() {
  if (--count <= 0) delete this;
}

//___________________________________________________________ rcString

// constructors and destructors

rcString::rcString() {
  rep = new rcStringRep(""); 
}

rcString::rcString(char c) {
  rep = new rcStringRep(c); 
}

rcString::rcString(const char* s) { 
  rep = new rcStringRep(s); 
}

rcString::rcString(const rcString &s) { 
  s.rep->ref(); rep = s.rep; 
}

rcString::rcString(_Char_p_* r) { 
  rep = new rcStringRep(r); 
}

rcString::~rcString() { 
  rep->deref(); 
}

// status reports

unsigned rcString::length() const {
  return rep->len; 
}

int rcString::empty() const { 
  return rep->len == 0; 
}

const char* rcString::chars() const {
  return rep->str; 
}

rcString::operator const char*() const {
  return rep->str; 
}

// element extraction and substring

char& rcString::operator[](unsigned i)
{
  if (i>=length())
    Error(ERR_PANIC, "rcString::operator[] index out of range");
  /*
   * Copy on write!
   */
  if (rep->count > 1) {
    rep->deref();
    rep = new rcStringRep(rep->str);
  }

  return rep->str[i];
}

char rcString::operator[](unsigned i) const
{
  if (i>=length())
    Error(ERR_PANIC, "rcString::operator[] index out of range");

  return rep->str[i];
}

rcString rcString::operator()(unsigned i, unsigned j)
{
  if (j<i || i>=length() || j>=length())
    Error(ERR_PANIC, "rcString::operator() index out of range");

  char* buf = new char[j-i+1];
  strncpy(buf, rep->str+i, j-i);
  buf[j-i] = '\0';

  return rcString(&buf);
}

// assignment

const rcString& rcString::operator=(char c)
{
  rep->deref();
  rep = new rcStringRep(c);

  return *this;
}

const rcString& rcString::operator=(const char* s)
{
  rep->deref();
  rep = new rcStringRep(s);

  return *this;
}

const rcString& rcString::operator=(const rcString& s)
{
  s.rep->ref();
  rep->deref();
  rep = s.rep;

  return *this;
}

// comparison operators

int rcString::operator==(const rcString& s) const { 
  return (strcmp(rep->str, s.rep->str) == 0); 
}

int rcString::operator==(const char* s) const { 
  return (strcmp(rep->str, s) == 0); 
}

int rcString::operator!=(const rcString& s) const { 
  return (strcmp(rep->str, s.rep->str) != 0); 
}

int rcString::operator!=(const char* s) const { 
  return (strcmp(rep->str, s) != 0); 
}

int rcString::operator<(const rcString& s) const { 
  return (strcmp(rep->str, s.rep->str) < 0); 
}

int rcString::operator<=(const rcString& s) const { 
  return (strcmp(rep->str, s.rep->str) <= 0); 
}

int rcString::operator>(const rcString& s) const { 
  return (strcmp(rep->str, s.rep->str) > 0); 
}

int rcString::operator>=(const rcString& s) const { 
  return (strcmp(rep->str, s.rep->str) >= 0); 
}

// concatenation

rcString operator+(const rcString& x, const char* y)
{
  char* buf = new char[x.length() + strlen(y) + 1];
  strcpy(buf, x.chars());
  strcat(buf, y);

  return rcString(&buf); // call the private constructor
}  

rcString operator+(const char* x, const rcString& y)
{
  char* buf = new char[strlen(x) + y.length() + 1];
  strcpy(buf, x);
  strcat(buf, y.chars());

  return rcString(&buf); // call the private constructor
}  

rcString operator+(const rcString& x, const rcString& y)
{
  char* buf = new char[x.length() + y.length() + 1];
  strcpy(buf, x.chars());
  strcat(buf, y.chars());

  return rcString(&buf); // call the private constructor
}  
  
// stream operators

ostream& operator<<(ostream& os, const rcString& s)
{ 
  return os << s.chars();
}
