//-----------------------------------------------------------
// Copyright Christian Arnault LAL-Orsay CNRS
// arnault@lal.in2p3.fr
// See the complete license in cmt_license.txt "http://www.cecill.info". 
//-----------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include "cmt_std.h"
#include "cmt_string.h"

cmt_string::cmt_string ()
{
  _data = 0;
  _allocated = 0;
  _size = 0;
}

cmt_string::cmt_string (int n)
{
  _data = 0;
  _allocated = 0;
  _size = 0;
  allocate (n + 1);
}

cmt_string::cmt_string (char c)
{
  _data = 0;
  _allocated = 0;
  _size = 0;

  allocate (2);

  _data[0] = c;
  _data[1] = 0;
  _size = 1;
}

cmt_string::cmt_string (const char* text)
{
  _data = 0;
  _allocated = 0;
  _size = 0;

  if (text != 0)
    {
      _size = strlen (text);
      allocate (_size + 1);
      strcpy (_data, text);
    }
}

cmt_string::cmt_string (const cmt_string& other)
{
  const char* text = other._data;

  _data = 0;
  _allocated = 0;
  _size = 0;

  if (text != 0)
    {
      _size = strlen (text);
      allocate (_size + 1);
      strcpy (_data, text);
    }
}

cmt_string::~cmt_string ()
{
  if (_data != 0)
    {
#ifdef CMT_USE_NEW_DELETE
      delete[] _data;
#else
      free (_data);
#endif
    }
  _data = 0;
  _allocated = 0;
  _size = 0;
}

  //
  // Operators
  //
cmt_string& cmt_string::operator = (char c)
{
  allocate (2);

  _data[0] = c;
  _data[1] = 0;

  _size = 1;

  return (*this);
}

cmt_string& cmt_string::operator = (const char* text)
{
  if (text == _data) return (*this);

  if (text != 0)
    {
      _size = strlen (text);
      allocate (_size + 1);
      strcpy (_data, text);
    }
  else
    {
      _size = 0;

      if (_data != 0)
        {
          _data[0] = 0;
        }
    }

  return (*this);
}

cmt_string& cmt_string::operator = (const cmt_string& other)
{
  const char* text = other._data;
  cmt_string& me = *this;
  me = text;
  return (me);
}

cmt_string::operator const char* () const
{
  if (_data == 0) return ("");
  else return (_data);
}

const char* cmt_string::c_str () const
{
  if (_data == 0) return ("");
  else return (_data);
}

/*
char* cmt_string::c_str ()
{
  return (_data);
}
*/

void cmt_string::operator += (char c)
{
  extend (2);

  char temp[2] = { c, 0 };

  strcat (&_data[_size], temp);
  _size++;
}

void cmt_string::operator += (const char* text)
{
  if (text == 0) return;

  int s = strlen (text);
  extend (s + 1);

  strcat (&_data[_size], text);
  _size += s;
}

void cmt_string::operator += (const cmt_string& other)
{
  const char* text = other._data;
  cmt_string& me = *this;

  me += text;
}

cmt_string cmt_string::operator + (char c) const
{
  cmt_string result (_data);
  result += c;

  return (result);
}

cmt_string cmt_string::operator + (const char* text) const
{
  cmt_string result (_data);
  result += text;

  return (result);
}

cmt_string cmt_string::operator + (const cmt_string& other) const
{
  cmt_string result (_data);
  result += other;

  return (result);
}

char cmt_string::operator [] (int index) const
{
  if ((_data == 0) ||
      (index < 0) ||
      (index >= _size))
    {
      return (0);
    }
  else
    {
      return (_data[index]);
    }
}

char& cmt_string::operator [] (int index)
{
  if ((_data == 0) ||
      (index < 0) ||
      (index >= _size))
    {
      static char temp;
      return (temp);
    }
  else
    {
      return (_data[index]);
    }
}

int cmt_string::size () const
{
  if (_data == 0) return (0);
  return (_size);
}

int cmt_string::size ()
{
  if (_data == 0) return (0);
  return (_size);
}

void cmt_string::resize (int n)
{
  allocate (n + 1);
}

int cmt_string::find (char c) const
{
  if (_data == 0) return (npos);

  char* p = strchr (_data, c);
  if (p == 0) return (npos);
  return (p - _data);
}

int cmt_string::find (const char* text) const
{
  if (_data == 0) return (npos);
  if (text == 0) return (npos);

  char* p = strstr (_data, text);
  if (p == 0) return (npos);
  return (p - _data);
}

int cmt_string::find (const cmt_string& other) const
{
  const char* text = other._data;
  return (find (text));
}

int cmt_string::find (int pos, char c) const
{
  if (_data == 0) return (npos);
  if (pos < 0) return (npos);
  if (pos >= _size) return (npos);

  char* p = strchr (&_data[pos], c);
  if (p == 0) return (npos);
  return (p - _data);
}

int cmt_string::find (int pos, const char* text) const
{
  if (_data == 0) return (npos);
  if (text == 0) return (npos);
  if (pos < 0) return (npos);
  if (pos >= _size) return (npos);

  char* p = strstr (&_data[pos], text);
  if (p == 0) return (npos);
  return (p - _data);
}

int cmt_string::find (int pos, const cmt_string& other) const
{
  const char* text = other._data;
  return (find (pos, text));
}

int cmt_string::find_last_of (char c) const
{
  if (_data == 0) return (npos);

  char* p = strrchr (_data, c);
  if (p == 0) return (npos);
  return (p - _data);
}

int cmt_string::find_last_of (const char* text) const
{
  if (_data == 0) return (npos);
  if (text == 0) return (npos);

  char* ptr = _data;
  char* last = 0;
  char* p;
  while ((p = strstr (ptr, text)) != 0)
    {
      last = p;
      ptr = p + 1;
    }
  if (last == 0) return (npos);
  return (last - _data);
}

int cmt_string::find_last_of (const cmt_string& other) const
{
  const char* text = other._data;
  return (find_last_of (text));
}

void cmt_string::erase (int pos)
{
  if ((_data == 0) ||
      (pos < 0) ||
      (pos >= _size))
    {
      return;
    }
  else
    {
      _data[pos] = 0;
      _size = pos;
    }
}

void cmt_string::erase (int pos, int length)
{
  if ((_data == 0) ||
      (pos < 0) ||
      (pos >= _size))
    {
      return;
    }
  else
    {
      if ((pos + length) >= _size)
        {
          _data[pos] = 0;
          _size = pos;
        }
      else if (length > 0)
        {
          char* d = &_data[pos];
	  char* s = &_data[pos + length];
	  for (;;)
            {
              *d = *s;
	      if (*s == 0) break;
	      d++;
	      s++;
	    }
          _size -= length;
        }
    }
}


void cmt_string::replace (const char* pattern, const char* replacement)
{
  if (_data == 0) return;
  if (_size == 0) return;
  if (pattern == 0) return;

  if (replacement == 0) replacement = "";

  if (pattern[0] == 0) return;

  int pattern_length = strlen (pattern);

  int replacement_length = strlen (replacement);
  int delta = replacement_length - pattern_length;

  int pos;

  if ((pos = find (pattern)) != npos)
    {
      if (delta > 0)
        {
            // string will be enlarged
          extend (delta);

          char* src = &_data[_size];
          char* dest = src + delta;
          while (src > &_data[pos])
            {
              *dest = *src;
              src--;
              dest--;
            }
        }
      else if (delta < 0)
        {
            // string will be shortened

          char* src = &_data[pos + pattern_length];
          char* dest = src + delta;
          while (*src != 0)
            {
              *dest = *src;
              src++;
              dest++;
            }
          *dest = *src;
        }

      strncpy (&_data[pos], replacement, replacement_length);

      _size += delta;
    }
}

void cmt_string::replace (const cmt_string& pattern,
                          const cmt_string& replacement)
{
  const char* p_text = pattern._data;
  const char* r_text = replacement._data;
  cmt_string& me = *this;

  me.replace (p_text, r_text);
}

void cmt_string::replace_all (const char* pattern, const char* replacement)
{
  if (_data == 0) return;
  if (_size == 0) return;
  if (pattern == 0) return;

  if (replacement == 0) replacement = "";

  if (pattern[0] == 0) return;

  int pattern_length = strlen (pattern);

  int replacement_length = strlen (replacement);
  int delta = replacement_length - pattern_length;

  int pos = 0;

  while ((pos = find (pos, pattern)) != npos)
    {
      if (delta > 0)
        {
            // string will be enlarged
          extend (delta);

          char* src = &_data[_size];
          char* dest = src + delta;
          while (src > &_data[pos])
            {
              *dest = *src;
              src--;
              dest--;
            }
        }
      else if (delta < 0)
        {
            // string will be shortened

          char* src = &_data[pos + pattern_length];
          char* dest = src + delta;
          while (*src != 0)
            {
              *dest = *src;
              src++;
              dest++;
            }
          *dest = *src;
        }

      strncpy (&_data[pos], replacement, replacement_length);
      pos += replacement_length;
      _size += delta;
    }
}

void cmt_string::replace_all (const cmt_string& pattern,
                              const cmt_string& replacement)
{
  const char* p_text = pattern._data;
  const char* r_text = replacement._data;
  cmt_string& me = *this;

  me.replace_all (p_text, r_text);
}

void cmt_string::trim ()
{
  if (size () == 0) return;

  int i = 0;

  i = strspn (_data, " \t");
  if (i > 0) erase (0, i);

  for (i = _size - 1; i >= 0; i--)
    {
      char c = _data[i];
      if ((c == ' ') || (c == '\t')) continue;
      erase (i + 1);
      break;
    }
}

cmt_string cmt_string::substr (int pos) const
{
  if ((_data == 0) ||
      (pos < 0) ||
      (pos >= _size))
    {
      return ((cmt_string) "");
    }
  else
    {
      return ((cmt_string) &_data[pos]);
    }
}

cmt_string cmt_string::substr (int pos, int length) const
{
  if ((_data == 0) ||
      (pos < 0) ||
      (pos >= _size))
    {
      return ((cmt_string) "");
    }
  else
    {
      cmt_string result (&_data[pos]);
      result.erase (length);
      return (result);
    }
}

void cmt_string::substr (int pos, cmt_string& dest) const
{
  if ((_data == 0) ||
      (pos < 0) ||
      (pos >= _size))
    {
      dest = "";
    }
  else
    {
      dest = (const char*) &_data[pos];
    }
}

void cmt_string::substr (int pos, int length, cmt_string& dest) const
{
  if ((_data == 0) ||
      (pos < 0) ||
      (pos >= _size))
    {
      dest = "";
    }
  else
    {
      dest = (const char*) &_data[pos];
      dest.erase (length);
    }
}

bool cmt_string::operator < (const char* text) const
{
  if (text == 0) return (false);
  if (_data == 0) return (false);

  if (strcmp (_data, text) < 0) return (true);
  return (false);
}

bool cmt_string::operator < (const cmt_string& other) const
{
  const char* text = other._data;
  const cmt_string& me = *this;

  return (me < text);
}

bool cmt_string::operator == (const char* text) const
{
  if (text == 0)
    {
      if (_data == 0) return (true);
      if (_size == 0) return (true);
      return (false);
    }
  if (_data == 0)
    {
      if (text == 0) return (true);
      if (text[0] == 0) return (true);
      return (false);
    }

  if (strcmp (_data, text) == 0) return (true);
  return (false);
}

bool cmt_string::operator == (const cmt_string& other) const
{
  const char* text = other._data;
  const cmt_string& me = *this;

  return (me == text);
}

bool cmt_string::operator != (const char* text) const
{
  const cmt_string& me = *this;

  if (!(me == text)) return (true);
  return (false);
}

bool cmt_string::operator != (const cmt_string& other) const
{
  const char* text = other._data;
  const cmt_string& me = *this;

  return (me != text);
}

bool cmt_string::operator > (const char* text) const
{
  if (text == 0) return (false);
  if (_data == 0) return (false);

  if (strcmp (_data, text) > 0) return (true);
  return (false);
}

bool cmt_string::operator > (const cmt_string& other) const
{
  const char* text = other._data;
  const cmt_string& me = *this;

  return (me > text);
}

void cmt_string::extend (int n)
{
  if (_data != 0) n += _size;
  allocate (n);
}

void cmt_string::allocate (int n)
{
  if ((n + 1) > _allocated)
    {
      static const int quantum = 128;
      int frames = ((n + 1)/quantum) + 1;
      _allocated = frames * quantum;

#ifdef CMT_USE_NEW_DELETE
      char* new_data = new char [_allocated + 1];
#else
      char* new_data = (char*) malloc (_allocated + 1);
#endif


      if (_data != 0)
        {
          strcpy (new_data, _data);

#ifdef CMT_USE_NEW_DELETE
          delete[] _data;
#else
          free (_data);
#endif

          _data = new_data;
        }
      else
        {
          new_data[0] = 0;
        }

      _data = new_data;
    }
}

ostream& operator << (ostream& o, const cmt_string& s)
{
  o << (const char*) s;
  return (o);
}

cmt_string operator + (const char* text, const cmt_string& s)
{
  cmt_string result = text;
  result += s;
  return (result);
}

cmt_string operator + (char c, const cmt_string& s)
{
  cmt_string result = c;
  result += s;
  return (result);
}

bool cmt_string::read (const cmt_string& file_name)
{
  FILE* f = fopen (file_name.c_str (), "rb");
  if (f != NULL)
    {
      fseek (f, 0L, SEEK_END);
      int size = ftell (f);
      fseek (f, 0L, SEEK_SET);

      allocate (size + 1);

      fread (&_data[0], size, 1, f);

      _data[size] = 0;
      _size = size;

      fclose (f);

      return (true);
    }
  else
    {
      cmt_string& me = *this;
      me = "";

      return (false);
    }
}

bool cmt_string::write (const cmt_string& file_name) const
{
  FILE* f = fopen (file_name.c_str (), "wb");
  if (f != NULL)
    {
      write (f);
      fclose (f);
      return (true);
    }
  else
    {
      return (false);
    }
}

void cmt_string::write (FILE* f) const
{
  fwrite (&_data[0], size (), 1, f);
}

void cmt_string::write (ostream& output)
{
  output.write (&_data[0], size ());
}

