/* Copyright (c) 2003-2005, 2007 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA */

/* -*- c-basic-offset: 4; -*- */
#include <ndb_global.h>
#include <BaseString.hpp>
#include "basestring_vsnprintf.h"

BaseString::BaseString()
{
    m_chr = new char[1];
    if (m_chr == NULL)
    {
      errno = ENOMEM;
      m_len = 0;
      return;
    }
    m_chr[0] = 0;
    m_len = 0;
}

BaseString::BaseString(const char* s)
{
    if (s == NULL)
    {
      m_chr = NULL;
      m_len = 0;
    }
    const size_t n = strlen(s);
    m_chr = new char[n + 1];
    if (m_chr == NULL)
    {
      errno = ENOMEM;
      m_len = 0;
      return;
    }
    memcpy(m_chr, s, n + 1);
    m_len = n;
}

BaseString::BaseString(const BaseString& str)
{
    const char* const s = str.m_chr;
    const size_t n = str.m_len;
    if (s == NULL)
    {
      m_chr = NULL;
      m_len = 0;
      return;
    }
    char* t = new char[n + 1];
    if (t == NULL)
    {
      errno = ENOMEM;
      m_chr = NULL;
      m_len = 0;
      return;
    }
    memcpy(t, s, n + 1);
    m_chr = t;
    m_len = n;
}

BaseString::~BaseString()
{
    delete[] m_chr;
}

BaseString&
BaseString::assign(const char* s)
{
    if (s == NULL)
    {
      m_chr = NULL;
      m_len = 0;
      return *this;
    }
    size_t n = strlen(s);
    char* t = new char[n + 1];
    if (t)
    {
      memcpy(t, s, n + 1);
    }
    else
    {
      errno = ENOMEM;
      n = 0;
    }
    delete[] m_chr;
    m_chr = t;
    m_len = n;
    return *this;
}

BaseString&
BaseString::assign(const char* s, size_t n)
{
    char* t = new char[n + 1];
    if (t)
    {
      memcpy(t, s, n);
      t[n] = 0;
    }
    else
    {
      errno = ENOMEM;
      n = 0;
    }
    delete[] m_chr;
    m_chr = t;
    m_len = n;
    return *this;
}

BaseString&
BaseString::assign(const BaseString& str, size_t n)
{
    if (n > str.m_len)
	n = str.m_len;
    return assign(str.m_chr, n);
}

BaseString&
BaseString::append(const char* s)
{
    size_t n = strlen(s);
    char* t = new char[m_len + n + 1];
    if (t)
    {
      memcpy(t, m_chr, m_len);
      memcpy(t + m_len, s, n + 1);
    }
    else
    {
      errno = ENOMEM;
      m_len = 0;
      n = 0;
    }
    delete[] m_chr;
    m_chr = t;
    m_len += n;
    return *this;
}

BaseString&
BaseString::append(char c) {
    return appfmt("%c", c);
}

BaseString&
BaseString::append(const BaseString& str)
{
    return append(str.m_chr);
}

BaseString&
BaseString::append(const Vector<BaseString> &vector,
		   const BaseString &separator) {
    for(size_t i=0;i<vector.size(); i++) {
	append(vector[i]);
	if(i<vector.size()-1)
	    append(separator);
    }
    return *this;
}

BaseString&
BaseString::assfmt(const char *fmt, ...)
{
    char buf[1];
    va_list ap;
    int l;

    /* Figure out how long the formatted string will be. A small temporary
     * buffer is used, because I don't trust all implementations to work
     * when called as vsnprintf(NULL, 0, ...).
     */
    va_start(ap, fmt);
    l = basestring_vsnprintf(buf, sizeof(buf), fmt, ap) + 1;
    va_end(ap);
    if(l > (int)m_len) {
        char *t = new char[l];
        if (t == NULL)
        {
          errno = ENOMEM;
          return *this;
        }
	delete[] m_chr;
	m_chr = t;
    }
    va_start(ap, fmt);
    basestring_vsnprintf(m_chr, l, fmt, ap);
    va_end(ap);
    m_len = strlen(m_chr);
    return *this;
}

BaseString&
BaseString::appfmt(const char *fmt, ...)
{
    char buf[1];
    va_list ap;
    int l;

    /* Figure out how long the formatted string will be. A small temporary
     * buffer is used, because I don't trust all implementations to work
     * when called as vsnprintf(NULL, 0, ...).
     */
    va_start(ap, fmt);
    l = basestring_vsnprintf(buf, sizeof(buf), fmt, ap) + 1;
    va_end(ap);
    char *tmp = new char[l];
    if (tmp == NULL)
    {
      errno = ENOMEM;
      return *this;
    }
    va_start(ap, fmt);
    basestring_vsnprintf(tmp, l, fmt, ap);
    va_end(ap);
    append(tmp);
    delete[] tmp;
    return *this;
}

BaseString&
BaseString::operator=(const BaseString& str)
{
    if (this != &str) {
	this->assign(str);
    }
    return *this;
}

int
BaseString::split(Vector<BaseString> &v,
		  const BaseString &separator,
		  int maxSize) const {
    char *str = strdup(m_chr);
    int i, start, len, num = 0;
    len = strlen(str);
    for(start = i = 0;
	(i <= len) && ( (maxSize<0) || ((int)v.size()<=maxSize-1) );
	i++) {
	if(strchr(separator.c_str(), str[i]) || i == len) {
	    if(maxSize < 0 || (int)v.size() < maxSize-1)
		str[i] = '\0';
	    v.push_back(BaseString(str+start));
	    num++;
	    start = i+1;
	}
    }
    free(str);

    return num;
}

ssize_t
BaseString::indexOf(char c) {
    char *p;
    p = strchr(m_chr, c);
    if(p == NULL)
	return -1;
    return (ssize_t)(p-m_chr);
}

ssize_t
BaseString::lastIndexOf(char c) {
    char *p;
    p = strrchr(m_chr, c);
    if(p == NULL)
	return -1;
    return (ssize_t)(p-m_chr);
}

BaseString
BaseString::substr(ssize_t start, ssize_t stop) {
    if(stop < 0)
	stop = length();
    ssize_t len = stop-start;
    if(len <= 0)
	return BaseString("");
    BaseString s;
    s.assign(m_chr+start, len);
    return s;
}

static bool
iswhite(char c) {
  switch(c) {
  case ' ':
  case '\t':
    return true;
  default:
    return false;
  }
  /* NOTREACHED */
}

char **
BaseString::argify(const char *argv0, const char *src) {
    Vector<char *> vargv;
    
    if(argv0 != NULL)
    {
      char *t = strdup(argv0);
      if (t == NULL)
      {
        errno = ENOMEM;
        return NULL;
      }
      if (vargv.push_back(t))
      {
        free(t);
        return NULL;
      }
    }
    
    char *tmp = new char[strlen(src)+1];
    if (tmp == NULL)
    {
      for(size_t i = 0; i < vargv.size(); i++)
        free(vargv[i]);
      errno = ENOMEM;
      return NULL;
    }
    char *dst = tmp;
    const char *end = src + strlen(src);
    /* Copy characters from src to destination, while compacting them
     * so that all whitespace is compacted and replaced by a NUL-byte.
     * At the same time, add pointers to strings in the vargv vector.
     * When whitespace is detected, the characters '"' and '\' are honored,
     * to make it possible to give arguments containing whitespace.
     * The semantics of '"' and '\' match that of most Unix shells.
     */
    while(src < end && *src) {
	/* Skip initial whitespace */
	while(src < end && *src && iswhite(*src))
	    src++;
	
	char *begin = dst;
	while(src < end && *src) {
	    /* Handle '"' quotation */
	    if(*src == '"') {
		src++;
		while(src < end && *src && *src != '"') {
		    if(*src == '\\')
			src++;
		    *dst++ = *src++;
		}
		src++;
		if(src >= end)
		    goto end;
	    }
	    
	    /* Handle '\' */
	    if(*src == '\\')
		src++;
	    else if(iswhite(*src))
		break;

	    /* Actually copy characters */
	    *dst++ = *src++;
	}
	
	/* Make sure the string is properly terminated */
	*dst++ = '\0';
	src++;

        {
          char *t = strdup(begin);
          if (t == NULL)
          {
            delete[] tmp;
            for(size_t i = 0; i < vargv.size(); i++)
              free(vargv[i]);
            errno = ENOMEM;
            return NULL;
          }
          if (vargv.push_back(t))
          {
            free(t);
            delete[] tmp;
            for(size_t i = 0; i < vargv.size(); i++)
              free(vargv[i]);
            return NULL;
          }
        }
    }
 end:
    
    delete[] tmp;
    if (vargv.push_back(NULL))
    {
      for(size_t i = 0; i < vargv.size(); i++)
        free(vargv[i]);
      return NULL;
    }
    
    /* Convert the C++ Vector into a C-vector of strings, suitable for
     * calling execv().
     */
    char **argv = (char **)malloc(sizeof(*argv) * (vargv.size()));
    if(argv == NULL)
    {
        for(size_t i = 0; i < vargv.size(); i++)
          free(vargv[i]);
        errno = ENOMEM;
	return NULL;
    }
    
    for(size_t i = 0; i < vargv.size(); i++){
	argv[i] = vargv[i];
    }
    
    return argv;
}

BaseString&
BaseString::trim(const char * delim){
    trim(m_chr, delim);
    m_len = strlen(m_chr);
    return * this;
}

char*
BaseString::trim(char * str, const char * delim){
    int len = strlen(str) - 1;
    for(; len > 0 && strchr(delim, str[len]); len--);
    
    int pos = 0;
    for(; pos <= len && strchr(delim, str[pos]); pos++);
    
    if(pos > len){
	str[0] = 0;
	return 0;
    } else {
	memmove(str, &str[pos], len - pos + 1);
	str[len-pos+1] = 0;
    }
    
    return str;
}

int
BaseString::vsnprintf(char *str, size_t size, const char *format, va_list ap)
{
  return(basestring_vsnprintf(str, size, format, ap));
}

int
BaseString::snprintf(char *str, size_t size, const char *format, ...)
{
  va_list ap;
  va_start(ap, format);
  int ret= basestring_vsnprintf(str, size, format, ap);
  va_end(ap);
  return(ret);
}


#ifdef TEST_BASE_STRING

/*
g++ -g -Wall -o tbs -DTEST_BASE_STRING -I$NDB_TOP/include/util \
        -I$NDB_TOP/include/portlib BaseString.cpp
valgrind ./tbs
*/

int main()
{
    BaseString s("abc");
    BaseString t(s);
    s.assign("def");
    t.append("123");
    assert(s == "def");
    assert(t == "abc123");
    s.assign("");
    t.assign("");
    for (unsigned i = 0; i < 1000; i++) {
	s.append("xyz");
	t.assign(s);
	assert(strlen(t.c_str()) % 3 == 0);
    }

    {
	BaseString s(":123:abc:;:foo:");
	Vector<BaseString> v;
	assert(s.split(v, ":;") == 7);

	assert(v[0] == "");
	assert(v[1] == "123");
	assert(v[2] == "abc");
	assert(v[3] == "");
	assert(v[4] == "");
	assert(v[5] == "foo");
	assert(v[6] == "");
    }

    {
	BaseString s(":123:abc:foo:bar");
	Vector<BaseString> v;
	assert(s.split(v, ":;", 4) == 4);

	assert(v[0] == "");
	assert(v[1] == "123");
	assert(v[2] == "abc");
	assert(v[3] == "foo:bar");

	BaseString n;
	n.append(v, "()");
	assert(n == "()123()abc()foo:bar");
	n = "";
	n.append(v);
	assert(n == " 123 abc foo:bar");
    }

    {
	assert(BaseString("hamburger").substr(4,2) == "");
	assert(BaseString("hamburger").substr(3) == "burger");
	assert(BaseString("hamburger").substr(4,8) == "urge");
	assert(BaseString("smiles").substr(1,5) == "mile");
	assert(BaseString("012345").indexOf('2') == 2);
	assert(BaseString("hej").indexOf('X') == -1);
    }

    {
	assert(BaseString(" 1").trim(" ") == "1");
	assert(BaseString("1 ").trim(" ") == "1");
	assert(BaseString(" 1 ").trim(" ") == "1");
	assert(BaseString("abc\t\n\r kalleabc\t\r\n").trim("abc\t\r\n ") == "kalle");
	assert(BaseString(" ").trim(" ") == "");
    }
    return 0;
}

#endif

template class Vector<char *>;
template class Vector<BaseString>;
