// Copyright (C) 1999-2005 Open Source Telecom Corporation.
//  
// 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; either version 2 of the License, or
// (at your option) any later version.
// 
// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// 
// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however    
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.    
//
// This exception applies only to the code released under the name GNU
// Common C++.  If you copy code from other releases into a copy of GNU
// Common C++, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU Common C++, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//

#include <cc++/config.h>
#include <cc++/export.h>
#include <cc++/thread.h>
#include <cc++/misc.h>
#include <cc++/strchar.h>
#include <cc++/string.h>
#include "private.h"

#ifdef	__BORLANDC__
#include <stdarg.h>
#include <stdio.h>
#else
#include <cstdarg>
#include <cstdio>
#endif

#ifdef	CCXX_NAMESPACES
namespace ost {
using std::streambuf;
using std::ofstream;
using std::ostream;
using std::endl;
using std::ios;
#endif

static Mutex mutex;
MemPager *String::pager = NULL;
char **String::idx = NULL;

const unsigned String::minsize = ((sizeof(char *) + (sizeof(unsigned) * 2) + 1));
const unsigned String::slotsize = 32;
const unsigned String::pagesize = 1024;
const unsigned String::slotlimit = 512;
const unsigned String::slotcount = ((slotlimit / slotsize) + 1);
const size_t String::npos = (size_t)(-1);

String::String()
{
	init();
}

String::String(std::string str)
{
       init();
       set(str.c_str());
}

String::String(const String &original)
{
	init();
	copy(original);
}

String::String(size_t chars, const char chr)
{
	init();
	resize(chars + 1);
	memset(getText(), chr, chars);
	setLength(chars);
}

#ifdef	HAVE_SNPRINTF
String::String(size_t chars, const char *format, ...)
{
        va_list args;
        va_start(args, format);

	init();
	resize(chars);

        char *ptr = getText();
	vsnprintf(ptr, chars, format, args);
        setLength(strlen(ptr));
	va_end(args);
}
#else
String::String(size_t chars, const char *str)
{
        init();
        resize(chars);

        if(!str || !*str)
                return;

        set(str);
}
#endif

String::String(const char *str)
{
	init();
	set(str);
}

String::String(const String &str, size_t start, size_t chars)
{
	init();
	char *ptr = str.getText();
	size_t len = str.getLength();

	if(start >= len)
		return;

	ptr += start;
	len -= start;
	
	if(chars >= len)
		chars = len;

	set(ptr, chars);
}

char String::at(ssize_t ind) const
{
	if(ind < 0)
		ind = (ssize_t)(getLength() - ind + 1);
	if((size_t)ind > getLength() || ind < 0)
		return 0;

	return (getText())[ind];
}

void String::insert(size_t start, const char *str, size_t chars)
{
	char *ptr = getText();
	size_t len = getLength();
	size_t sz = getSize();

	if(!str)
		str = "";

	if(!chars)
		chars = strlen(str);

	if(!chars || start > len)
		return;

	if(len + chars >= sz)
	{
		resize(len + chars + 1);
		ptr = getText();
	}

	if(start == len)
	{
		memmove(ptr + start, str, chars);
		len += chars;
		setLength(len);
		ptr[len] = 0;
		return;
	}

	memmove(ptr + start + chars, ptr + start, len - start);
	memmove(ptr + start, str, chars);
	len += chars;
	setLength(len);
	ptr[len] = 0;
	return;
}	


void String::insert(size_t start, const String &s)
{
	insert(start, s.getText(), s.getLength());
}

void String::replace(size_t start, size_t len, const char *cp, size_t chars)
{
	erase(start, len);
	insert(start, cp, chars);
}

void String::replace(size_t start, size_t len, const String &s)
{
	erase(start, len);
	insert(start, s);
}

void String::erase(size_t start, size_t chars)
{
	size_t len = getLength();
	char *ptr = getText();
		
	if(start >= len)
		return;

	if(start + chars >= len || chars == npos || !chars)
	{
		setLength(start);
		ptr[start] = 0;
		return;
	}		

	memmove(ptr + start, ptr + start + chars, len - start - chars);
	len -= chars;
	setLength(len);
	ptr[len] = 0;
}

void String::append(const char *str, size_t offset, size_t len)
{
	size_t slen = getLength();
	char *ptr = getText();

	if(slen < offset)
	{
		append(str, len);
		return;
	}

	setLength(offset);
	ptr[offset] = 0;
	append(str, len);
}

void String::append(const char *str, size_t len)
{
	if(!str)
		return;

	if(!len)
		len = strlen(str);

	if(!len)
		return;

	if(getLength() + len >= getSize())
		resize(getLength() + len + 1);
	memmove(getText() + getLength(), str, len);
	len += getLength();
	setLength(len);
	getText()[len] = 0;
	return;
}

void String::add(char c)
{
	size_t len = getLength();
	char *ptr;

	if(len + 1 >= getSize())
		resize(len + 2);
	
	ptr = getText();
	ptr[len++] = c;
	setLength(len);
	ptr[len] = 0;
}

void String::trim(size_t chars)
{
	size_t len = getLength();
	char *ptr = getText();

	if(chars >= len)
		chars = len;

	len -= chars;
	ptr[len] = 0;
	setLength(len);
}

String String::token(const char *delim, size_t offset)
{
        char *ptr = getText();
        size_t len = getLength();
        size_t chars = 0;
        String result;
	bool found = false;

	if(offset >= len)
		return result;

	len -= offset;
	ptr += offset;
                                                                        
        while(chars < len)
        {
                if(strchr(delim, ptr[chars]))
		{
			found = true;
                        break;
		}
                ++chars;
        }

	if(!chars && found)
		erase(offset, 1);
                                                                        
        if(!chars)
		return result;

        result.set(ptr, chars);                                                                        
	if(found)
		++chars;
        erase(offset, chars);
	return result;
}

void String::strip(const char *chars)
{
	size_t len = strtrim(chars, getText(), getLength());
	if(!len)
	{
		setLength(len);
		return;
	}
	setLength(strchop(chars, getText(), len));
}

#ifdef	HAVE_SNPRINTF
const char *String::set(size_t chars, const char *format, ...)
{
        va_list args;
        va_start(args, format);

	if(chars <= minsize)
		clear();

	if(chars > getSize())
		resize(chars);

        char *ptr = getText();
	vsnprintf(ptr, chars, format, args);
        setLength(strlen(ptr));
	va_end(args);
	return ptr;
}

void String::append(size_t chars, const char *format, ...)
{
        va_list args;
        va_start(args, format);
	size_t len = getLength();

	if(len + chars <= minsize)
		clear();

	if(len + chars > getSize())
		resize(len + chars);

        char *ptr = getText() + len;
	vsnprintf(ptr, chars, format, args);
        setLength(strlen(getText()));
	va_end(args);
}

#endif
const char *String::set(const char *str, size_t len)
{
	if(!str)
	{
		clear();
		return str;
	}

	if(!len)
		len = strlen(str);

	// if we are making a short string, lets clear prior alloc

	if(len < minsize)
		clear();

	if(len >= getSize())
		resize(len + 1);
	memmove(getText(), str, len);
	getText()[len] = 0;
	setLength(len);
	return str;
}

void String::set(const String &str)
{
	set(str.getText(), str.getLength());
}

void String::append(const String &str)
{
	append(str.getText(), str.getLength());
}


void String::copy(const String &original)
{
	clear();

	if(original.getLength() < minsize)
	{
		content.ministring.length = (unsigned)original.getLength();
		memmove(content.ministring.text, original.getText(), original.getLength() + 1);
		content.ministring.big = false;
		return;
	}

//	ptr = original.getText();
	content.bigstring.length = original.getLength();
	content.bigstring.size = setSize(original.getLength() + 1);
	content.bigstring.text = getSpace(content.bigstring.size);
	content.ministring.big = true;
	memmove(content.bigstring.text, original.getText(), original.getLength() + 1);
}

String::~String()
{
	clear();
}

void String::init(void)
{
	content.ministring.big = false;
	content.ministring.length = 0;
	content.ministring.text[0] = 0;
}

size_t String::search(const char *cp, size_t clen, size_t ind) const
{
	size_t len = getLength();
	 
	if(!cp)
		cp = "";

	if(!clen)
		clen = strlen(cp);

	while(clen + ind <= len)
	{
		if(compare(cp, clen, ind) == 0)
			return ind;
		++ind;
	}
	return npos;
}

size_t String::find(const char *cp, size_t ind, size_t len, unsigned instance) const
{
	size_t pos;

	if(!cp)
		cp = "";

	if(!len)
		len = strlen(cp);

	while(instance--)
	{
		pos = search(cp, len, ind);
		if(pos == npos)
			break;
		ind = pos + 1;
	}
	return pos;
}

size_t String::rfind(const char *cp, size_t ind, size_t len) const
{
	size_t result = npos;

        if(!cp)
                cp = "";

        if(!len)
                len = strlen(cp);

	for(;;)
	{
		ind = search(cp, len, ind);
		if(ind == npos)
			break;
		result = ind++;
	}
	return result;
}

unsigned String::count(const char *cp, size_t ind, size_t len) const
{
	unsigned chars = 0;
	if(!cp)
		cp = "";

	if(!len)
		len = strlen(cp);

	for(;;)
	{
		ind = search(cp, len, ind);
		if(ind == npos)
			break;
		++chars;
		++ind;
	}
	return chars;
}

unsigned String::count(const String &str, size_t ind) const
{
	return count(str.getText(), ind, str.getLength());
}

size_t String::find(const String &str, size_t ind, unsigned instance) const
{
	return find(str.getText(), ind, str.getLength(), instance);
}

size_t String::rfind(const String &str, size_t ind) const
{
	return rfind(str.getText(), ind, str.getLength());
}

int String::compare(const char *cp, size_t len, size_t ind) const
{
	if(!cp)
		cp = "";

	if(ind > getLength())
		return -1;

	if(!len)
		return strcmp(getText() + ind, cp);

	return strncmp(getText() + ind, cp, len);
}

bool String::isEmpty(void) const
{
	char *ptr = getText();

	if(!ptr || !*ptr)
		return true;

	return false;
}

void String::resize(size_t chars)
{
	size_t len = getLength();
	char *ptr;

	if(len >= chars)
		len = chars - 1;

	++len;

	if(!isBig() && chars <= minsize)
		return;

	if(!isBig())
	{
		ptr = getSpace(chars);
		memmove(ptr, content.ministring.text, len); 
		ptr[--len] = 0;
		content.ministring.big = true;
		content.bigstring.text = ptr;
		content.bigstring.length = len;
		setSize(chars);
		return;
	}
	
	if(chars <= minsize && getSize() > slotlimit)
	{
		ptr = getText();
		memmove(content.ministring.text, ptr, len);
		content.ministring.text[--len] = 0;
		content.ministring.big = false;
		content.ministring.length = (unsigned)len;
		delete[] ptr;
		return;
	}

	ptr = getSpace(chars);
	memmove(ptr, getText(), len);
	ptr[--len] = 0;
	clear();
	setSize(chars);
	content.bigstring.length = len;
	content.bigstring.text = ptr;
	content.ministring.big = true;
}

void String::clear(void)
{
	char **next;
	unsigned slot;

	if(!isBig())
		goto end;

	if(!content.bigstring.text)
		goto end; 

	if(getSize() > slotlimit)
	{
		delete[] content.bigstring.text;
		goto end;
	}

	slot = ((unsigned)getSize() - 1) / slotsize;
	next = (char **)content.bigstring.text;
	mutex.enterMutex();
	*next = idx[slot];
	idx[slot] = content.bigstring.text;
	setLength(0);
	content.bigstring.text = NULL;
	mutex.leaveMutex();	
	
end:
	init();
	return;
}	

char *String::getSpace(size_t chars)
{
	unsigned slot;
	char *cp, **next;

	if(chars > slotlimit)
		return new char[chars];

	slot = (unsigned)chars / slotsize;
	mutex.enterMutex();
	if(!pager)
	{
		pager = new MemPager(pagesize);
		idx = (char **)pager->alloc(sizeof(char *) * slotcount);
		memset(idx, 0, sizeof(char *) * slotcount);
	}
	cp = idx[slot];
	if(cp)
	{
		next = (char **)cp;
		idx[slot] = *next;
	}
	else
		cp = (char *)pager->alloc(++slot * slotsize);
	mutex.leaveMutex();
	return cp;
}

const char *String::getIndex(size_t ind) const
{
	const char *dp = getText();

	if(ind > getLength())
		return NULL;

	return (const char *)dp + ind;
}
		
char *String::getText(void) const
{
	if(isBig())
		return (char *)content.bigstring.text;

	return (char *)content.ministring.text;
}

long String::getValue(long def) const
{
	unsigned base = 10;
	char *cp = getText();
	char *ep = 0;
	long val;

	if(!cp)
		return def;

	if(!strnicmp(cp, "0x", 2))
	{
		cp += 2;
		base = 16;
	}

	val = ::strtol(cp, &ep, base);
	if(!ep || *ep)
		return def;
	return val;
}

bool String::getBool(bool def) const
{
	char *cp = getText();

	if(!cp)
		return def;

	if(isdigit(*cp))
	{
		if(!getValue(0))
			return false;
		return true;
	}

	if(!stricmp(cp, "true") || !stricmp(cp, "yes"))
		return true;

	if(!stricmp(cp, "false") || !stricmp(cp, "no"))
		return false;

	return def;
}		
	
const size_t String::getSize(void) const
{
	if(isBig())
		return content.bigstring.size;

	return minsize;
}

void String::setLength(size_t len)
{
	if(isBig())
		content.bigstring.length = len;
	else
		content.ministring.length = (unsigned)len;
}

size_t String::setSize(size_t chars)
{
	if(chars <= minsize && !isBig())
		return minsize;

  if(chars <= slotlimit)
  {
    size_t slotcount = chars / slotsize;
    if((chars % slotsize)!=0) ++slotcount;
    chars = slotcount*slotsize;
  }
	content.bigstring.size = chars;
	return chars;
}

const size_t String::getLength(void) const
{
	if(isBig())
		return content.bigstring.length;

	return content.ministring.length;
}

String operator+(const String &s1, const char c2)
{
	String result(s1);
	result.add(c2);
	return result;
}

String operator+(const String &s1, const String &s2)
{
	String result(s1);
	result.append(s2);
	return result;
}

String operator+(const String &s1, const char *s2)
{
	String result(s1);
	result += s2;
	return result;
}

String operator+(const char *s1, const String &s2)
{
	String result(s1);
	result += s2;
	return result;
}

String operator+(const char c1, const String &s2)
{
	String result(1, c1);
	result += s2;
	return result;
}

bool String::operator<(const String &s1) const
{
        if(compare(s1.getText()) < 0)
                return true;

        return false;
}

bool String::operator<(const char *s1) const
{
        if(compare(s1) < 0)
                return true;

        return false;
}

bool String::operator>(const String &s1) const
{
        if(compare(s1.getText()) > 0)
                return true;

        return false;
}

bool String::operator>(const char *s1) const
{
        if(compare(s1) > 0)
                return true;

        return false;
}

bool String::operator<=(const String &s1) const
{
	if(compare(s1.getText()) <= 0)
		return true;

	return false;
}

bool String::operator<=(const char *s1) const
{
        if(compare(s1) <= 0)
                return true;

        return false;
}

bool String::operator>=(const String &s1) const
{
        if(compare(s1.getText()) >= 0)
                return true;

        return false;
}

bool String::operator>=(const char *s1) const
{
        if(compare(s1) >= 0)
                return true;

        return false;
}

bool String::operator==(const String &s1) const
{
        if(compare(s1.getText()) == 0)
                return true;

        return false;
}

bool String::operator==(const char *s1) const
{
        if(compare(s1) == 0)
                return true;

        return false;
}

bool String::operator!=(const String &s1) const
{
        if(compare(s1.getText()) != 0)
                return true;

        return false;
}

bool String::operator!=(const char *s1) const
{
        if(compare(s1) != 0)
                return true;

        return false;
}

bool String::operator*=(const String &s1) const
{
	return search(s1.getText(), s1.getLength()) != npos;
}

bool String::operator*=(const char *s) const
{
	return search(s) != npos;
}

std::ostream &operator<<(std::ostream &os, const String &str)
{
	os << str.getText();
	return os;
}

void *StringObject::operator new(size_t size) NEW_THROWS
{
	char *base;
	size_t *sp;
	size += sizeof(size_t);

	if(size > String::slotlimit)
		return NULL;

	base = String::getSpace(size);

	if(!base)
		return NULL;

	sp = (size_t *)base;
	*sp = size;
	base += sizeof(size_t);
	return (void *)base;
}	

void StringObject::operator delete(void *ptr)
{
	char **next;
	unsigned slot;
	size_t *size = (size_t *)ptr;
	
	--size;
	ptr = size;

	slot = (unsigned)(((*size) - 1) / String::slotsize);
	next = ((char **)(ptr));
	mutex.enter();
	*next = String::idx[slot];
	String::idx[slot] = (char *)ptr;
	mutex.leave();
}
	
	

std::istream &getline(std::istream &is, String &str, char delim, size_t len)
{
	if(!len)
		len = str.getSize() - 1;

	if(len >= str.getSize())
		str.resize(len + 1);

	char *ptr = str.getText();
	is.getline(ptr, (std::streamsize)len, delim);
	str.setLength(strlen(ptr));
	return is;
}

SString::SString() :
String(), streambuf()
#ifdef  HAVE_OLD_IOSTREAM
,ostream()
#else
,ostream((streambuf *)this)
#endif
{
#ifdef  HAVE_OLD_IOSTREAM
        ostream::init((streambuf *)this);
#endif
}

SString::SString(const SString &from) :
String(from), streambuf()
#ifdef  HAVE_OLD_IOSTREAM
,ostream()
#else
,ostream((streambuf *)this)
#endif
{
#ifdef  HAVE_OLD_IOSTREAM
        ostream::init((streambuf *)this);
#endif
}

SString::~SString()
{
	if(isBig())
		String::clear();
}

int SString::overflow(int c)
{
	String::add((char)(c));
	return c;
}

#ifdef	HAVE_SNPRINTF
int strprintf(String &str, size_t size, const char *format, ...)
{
	va_list args;
	va_start(args, format);
	int rtn;

	if(!size)
		size = str.getSize();

	if(size > str.getSize())
		str.resize(size);

	char *ptr = str.getText();
	str.setLength(0);
	ptr[0] = 0;
	rtn = vsnprintf(ptr, size, format, args);
	str.setLength(strlen(ptr));
	va_end(args);
	return rtn;
}
#endif

#ifdef	CCXX_NAMESPACES
}
#endif

/** EMACS **
 * Local variables:
 * mode: c++
 * c-basic-offset: 8
 * End:
 */
