// C++ code Copyright (C) David R. Evans G4AMJ/NQ0I

#include <assert.h>
#include <ctype.h>
#include <stdlib.h>
#include <iostream.h>

#include <DREstring.h>

// initialiser used in all constructors
void DREstring::initialise(void)
{ heap_check(p = new str);
  p->max_len = 0; 
  p->len = 0;
  p->s = 0;
  p->ref = 1;
}

// generic constructor
DREstring::DREstring(void) { this->initialise(); }

// constructor with int
DREstring::DREstring(const int max_length)
{ this->initialise();
  p->max_len = max_length; 
}

// constructor with long, int (int to DREstring type conversion)
DREstring::DREstring(const long value, int radix)
{ char temp[20];
  this->initialise();
  *this = ltoa(value, temp, radix);
}

// constructor with char*
DREstring::DREstring(const char* init_DREstring)
{ // cout << "Constructing with : " << init_DREstring << "\n";
  this->initialise();
  *this = init_DREstring;
}

// constructor with char
DREstring::DREstring(const char c)
{ this->initialise();
  heap_check(p->s = new char [2]);  // !
  *(p->s) = c;
  *(p->s + 1) = 0;
  p->len = 1;
}

// constructor with DREstring
DREstring::DREstring(DREstring& rhs)
{ rhs.p->ref++;
  p = rhs.p;
}

// constructor with double, const char*
DREstring::DREstring(double value, const char* format)
{ char temp[100];
  this->initialise();
  sprintf(temp, format, value);
  *this = temp;
}

// constructor with double, double
DREstring::DREstring(double value, double format)
{ char t1[100];
  sprintf(t1, "%1.1f", format);
  DREstring t2 = "%";
  t2 += (DREstring)t1;
  t2 += (DREstring)"f";
  this->initialise();
  *this = DREstring(value, (const char*)t2);
}

// constructor with int, const char*
DREstring::DREstring(int value, const char* format)
{ char temp[100];
  this->initialise();
  sprintf(temp, format, value);
  *this = temp;
}

// constructor with char*, int
DREstring::DREstring(char* byte_array, int n_bytes)
{ this->initialise();
  heap_check(p->s = new char [n_bytes + 1]);
  for (int n = 0; n < n_bytes; n++)
    *(p->s + n) = byte_array[n];
  *(p->s + n_bytes) = 0;
  p->len = n_bytes;
}

// destructor
DREstring::~DREstring()
{ if (!(p->ref)) then
    if (p->s) then
      fatal_error("Null reference count");
  if (!(--p->ref)) then
  { if (p->s) delete [] p->s;
    delete p;
  }
}

// DREstring[n]
char& DREstring::operator[](int n)
{ // if expression is on lhs, we must only change this reference
  if (p->ref > 1) then
  { DREstring temp = *this;
    this->disconnect();
    p->len = temp.length();
    heap_check(p->s = new char [p->len + 1]);
    for (int k = 0; k <= temp.length(); k++)
      *(p->s + k) = *(temp.p->s + k);
  }
  return *(p->s + n - 1);
}

// DREstring = char*
void DREstring::operator=(const char* rhs)
{ if (p->ref > 1) then
    this->disconnect();
  else
    { if (p->s) delete [] p->s; }
  p->len = (((strlen(rhs) <= p->max_len) || (p->max_len == 0)) ?
strlen(rhs) : p->max_len);
  heap_check(p->s = new char [p->len + 1]);
  for (int n = 0; n <= p->len; n++)    // keep the end-of-DREstring null !!
    *(p->s+n) = *(rhs+n);
  *(p->s + p->len) = '\0';
}

// DREstring = DREstring
void DREstring::operator=(DREstring& rhs)
{ if (this == &rhs) then
    return;

  if (p->ref > 1) then
    disconnect();
  else
    { if (p->s) delete [] p->s; }

  p->len = (((rhs.length() <= p->max_len) || (p->max_len == 0)) ?
                            rhs.length() : p->max_len);
  char *rhs_ptr = rhs.p->s;
  heap_check(p->s = new char [p->len + 1]);
  for (int n = 0; n < p->len; n++)
    *(p->s + n) = *(rhs_ptr + n);
  *(p->s + p->len) = '\0';
}

// DREstring += DREstring
void DREstring::operator+=(DREstring& rhs)
{ int total_length = p->len + rhs.p->len,
      original_length = p->len;
  if ((p->max_len) && (total_length > p->max_len)) then
    total_length = p->max_len;
  char* buf = p->s;
  boolean multiple = false;
  
  if (p->ref > 1) then
  { disconnect();
    multiple = true;
  }

  heap_check(p->s = new char[total_length + 1]);
  for (int n = 0; n < original_length; n++)
    *(p->s+n) = *(buf+n);
  for (n = 0; n < (total_length - original_length); n++)
    *(p->s + original_length + n) = rhs.chr(n + 1);
  *(p->s + total_length) = '\0';

  if (!multiple) then
    { if (buf) delete [] buf; }

  p->len = total_length;
}

// DREstring += const char*
void DREstring::operator+=(const char* rhs)
{ int total_length = p->len + strlen(rhs),
      original_length = p->len;
  if ((p->max_len) && (total_length > p->max_len)) then
    total_length = p->max_len;
  char* buf = p->s;
  boolean multiple = false;
  
  if (p->ref > 1) then
  { disconnect();
    multiple = true;
  }

  heap_check(p->s = new char[total_length + 1]);
  for (int n = 0; n < original_length; n++)
    *(p->s+n) = *(buf+n);
  for (n = 0; n < (total_length - original_length); n++)
    *(p->s + original_length + n) = rhs[n];
  *(p->s + total_length) = '\0';

  if (!multiple) then
    { if (buf) delete [] buf; }

  p->len = total_length;
}

// DREstring += char  or  DREstring += int
void DREstring::operator+=(char& the_char)
{ char array[2];
  array[0] = the_char;  array[1] = '\0';
  const char* char_ptr = array;
  *this += char_ptr;
}

// equality operator
int DREstring::operator==(DREstring& rhs)
{ int equal = 1;
  equal = (equal && (length() == rhs.length()));
  if (equal) then
    { for (int n = 0; ((equal) && (n < length())); n++)
      equal = (*(p->s+n) == *(rhs.p->s+n));
    }
  return equal;
}

// Borland C++ 3.1 will not permit this function to exist
// (It could of course be emulated by using a different name, but then
// we lose the syntactic nicety of using the equals operator)
// char* = DREstring. If char* is to NULL then changes it and copies DREstring,
//                  otherwise, DREstring must fit in the area pointed to by char*
/*
void operator=(char* lhs, const DREstring& rhs) // changes where lhs points iff lhs NULL
{ if (!lhs) then lhs = new char [rhs.length() + 1]; // change pointer if NULL
  for (int n = 0; n < rhs.length(); n++) *(lhs+n) = rhs.chr(n + 1);
  *(lhs + rhs.length()) = '\0';
}
*/

// forced type conversion to char. This is necessary because g++ handles
// char* = DREstring as meaning char* = (char*)DREstring; I believe that this
// behaviour is incorrect!
void copy(char* cp, DREstring& st)
{ if (!cp) then
    heap_check(cp = new char [st.length() + 1]);
  for (int n = 0; n < st.length(); n++)
    *(cp + n) = *(st.p->s+n);
  *(cp + st.length()) = '\0';
}

// DREstring + DREstring
DREstring operator+(DREstring& lhs, DREstring& rhs)
{ DREstring plus_result = lhs;
  plus_result += rhs;
  return plus_result;
}

// disconnect a DREstring from itself
void DREstring::disconnect()
{ p->ref--;
  str* temp = p;
  heap_check(p = new str);
  p->max_len = temp->max_len;
  p->ref = 1;
  p->s = 0;
  temp = 0;
}

void replace(DREstring& the_DREstring, char* old_char, char* new_char, const int
n = 0)
{ int n_replacements = (n ? n : 10000);
  int index = 1;
  for (int m = 0; ((m < n_replacements) && (index <= the_DREstring.length()));
index++)
  { if (the_DREstring[index] == *old_char) then
    { the_DREstring[index] = *new_char;
      m++;
    }
  }
}

DREstring left_fill(DREstring& the_DREstring, const int total_width,
                 const char* insert_char)
{ DREstring left_fill_result;
  const int n_to_insert = total_width - the_DREstring.length();
  for (int n = 1; n <= n_to_insert; n++)
    left_fill_result += insert_char;
  left_fill_result += the_DREstring;
  return left_fill_result;
}

DREstring right_fill(DREstring& the_DREstring, const int total_width,
                 const char* insert_char)
{ DREstring right_fill_result;
  const int n_to_insert = total_width - the_DREstring.length();
  right_fill_result = the_DREstring;
  for (int n = 1; n <= n_to_insert; n++)
    right_fill_result += insert_char;
  return right_fill_result;
}

DREstring centre(DREstring& the_DREstring, const int total_width,
              const char* surround_char)
{ DREstring centre_result = the_DREstring;
  const int padding_size = total_width - the_DREstring.length();
  centre_result = left_fill(centre_result, padding_size / 2 + 
                            the_DREstring.length(), surround_char);
  centre_result = right_fill(centre_result, total_width, surround_char);
  return centre_result;
}

// a DREstring of a repeated character DREstring : total length = n
DREstring filled_DREstring(char* the_char, int n)
{ DREstring filled_DREstring_result(n);
  while (filled_DREstring_result.length() < n)
    filled_DREstring_result += (DREstring)the_char;
  return filled_DREstring_result;
}

// return subDREstring
DREstring subDREstring(DREstring& the_DREstring, const int start, const int len)
{ DREstring subDREstring_result;
  int true_start = ((start < 1) ? 1 : start);
  if (true_start > the_DREstring.length()) then
    return subDREstring_result;
  int end = start + len - 1;
  int true_end = ((end > the_DREstring.length()) ? the_DREstring.length() : end);
  for (int n = true_start; n <= true_end; n++)
    subDREstring_result += (DREstring)(the_DREstring.chr(n));
  return subDREstring_result; 
}

// find a given character
int find(DREstring& the_DREstring, const char* the_char)
{ for (int n = 1;
       ((n <= the_DREstring.length()) && (the_DREstring.chr(n) != *the_char));
       n++);;
  if (n <= the_DREstring.length()) then
    return n;
  else
    return 0;
}

int find(DREstring& the_DREstring, char& the_char)
{ return find(the_DREstring, (char*)(&the_char)); }

// insert one DREstring into another
DREstring insert(DREstring& DREstring1, int pos, DREstring& DREstring2)
{ if (pos > DREstring1.length()) then
    return DREstring1;
  DREstring insert_result = subDREstring(DREstring1, 1, pos - 1);
  int max_len = (DREstring1.max_length() ? DREstring1.max_length(): 10000);
  int chars_from_DREstring2 = (((pos - 1 + DREstring2.length()) > max_len) ?
(DREstring1.max_length() - (pos - 1)) : DREstring2.length());
  insert_result += subDREstring(DREstring2, 1, chars_from_DREstring2);
  int chars_from_DREstring1 = (((DREstring1.length() + DREstring2.length()) >
max_len) ?
(DREstring1.length() + DREstring2.length() - insert_result.length()) :
DREstring1.length() - (pos - 1));
  insert_result += subDREstring(DREstring1, pos, chars_from_DREstring1);
  return insert_result;
}

// overwrite one DREstring with another : length NOT extended
DREstring overwrite(DREstring& DREstring1, const int pos, DREstring& DREstring2)
{ if (pos > DREstring1.length()) then
    return DREstring1;
  DREstring overwrite_result = subDREstring(DREstring1, 1, pos - 1);
  int chars_from_DREstring2 = ((pos - 1 + DREstring2.length() > DREstring1.length())
? (DREstring1.length() - (pos - 1)) : DREstring2.length());
  overwrite_result += subDREstring(DREstring2, 1, chars_from_DREstring2);
  int chars_from_DREstring_1 = DREstring1.length() - DREstring2.length() - (pos -
1);
  overwrite_result += subDREstring(DREstring1, pos + DREstring2.length(),
chars_from_DREstring_1);
  return overwrite_result;
}

// remove a subDREstring from a DREstring
DREstring remove(DREstring& the_DREstring, int pos, int len)
{ DREstring remove_result = subDREstring(the_DREstring, 1, pos - 1);
  remove_result += subDREstring(the_DREstring, pos + len);
  return remove_result;
}

// duplicate(destination, source) -- make a duplicate of a DREstring
// a call to this routine should NEVER be necessary
void duplicate(DREstring& dest, DREstring src)
{ // clear the destination DREstring
  if (dest.p->ref > 1) then dest.disconnect();
  dest.p->len = src.p->len;
  dest.p->max_len = src.p->max_len;
  dest.p->ref = 1;
  heap_check(dest.p->s = new char [src.length() + 1]);	// allow room for final zero
  for (int n = 0; n <= src.length(); n++)
    *(dest.p->s+n) = *(src.p->s+n);
}

void DREstring::operator++(void)
{ p->ref++; }

ostream& operator<<(ostream& ost, const DREstring s)
{ const char* tc = s;
  ost << tc;
  return ost;
}

// read a line into a DREstring; the newline is NOT included
istream& operator>>(istream& ist, DREstring& s)
{ char c;
  uint32 permitted_length = s.max_length();
  if (!permitted_length)
    permitted_length = 100000;   // arbitrary and large
  s = "";
  
  while (s.length() < permitted_length)
  { ist.get(c);
    if ((c == '\n') || !(ist.good())) then
      return ist;
    else
      s += c;
  }

// the line was too long
  warning("Incomplete line read in operator>>(istream&, DREstring&)");
  return ist;
}

DREstring& DREstring::toupper(void)
{ for (int n = 1; n <= this->length(); n++)
    (*this)[n] = ::toupper(chr(n));
  return *this;
}

DREstring DREstring::operator+(const char* cp)
{ DREstring plus_result = *this;
  plus_result += cp;
  return plus_result;
}


