/* ====================================================================
 * Copyright (c) 2003-2007, Martin Hauner
 *                          http://subcommander.tigris.org
 *
 * Subcommander is licensed as described in the file doc/COPYING, which
 * you should have received as part of this distribution.
 * ====================================================================
 */

// sc
#include "String.h"
#include "utf8.h"

// sys
#include <string.h>
#include <stdlib.h>

#define max(a, b)  (((a) > (b)) ? (a) : (b)) 
#define min(a, b)  (((a) < (b)) ? (a) : (b)) 


namespace sc
{

sc::String operator+( const char *cin, const sc::String& in )
{
  return sc::String(cin) + in;
}


String::String()
: _str(0), _chars(0), _bytes(0)
{
}

String::String( const String& src )
: _str(0), _chars(0), _bytes(0)
{
  *this =  src;
}

String::String( const char* str )
: _str(0), _chars(0), _bytes(0)
{
  _copy(str);
  _bytes = _strbytes(str);
  _chars = utf8::strlen8(str,_bytes);
}

String::String( const char* str, Size bytes )
: _str(0), _chars(0), _bytes(0)
{
  _ncopy(str,bytes);
  _bytes = bytes;
  _chars = utf8::strlen8(str,bytes);
}

String::~String()
{
  _free(_str);
}

String::operator const char*() const
{
  return _str;
}

void String::operator=( const String& in )
{
  _ncopy( in._str, in._bytes );
  _chars = in._chars;
  _bytes = in._bytes;
}

void String::operator=( const char* in )
{
  _copy(in);
  _bytes = _strbytes(in);
  _chars = utf8::strlen8(in,_bytes);
}

const char* String::getStr() const
{
  return _str;
}

size_t String::getCharCnt() const
{
  return _chars;
}

size_t String::getByteCnt() const
{
  return _bytes;
}

bool String::isEmpty() const
{
  return getCharCnt() == 0;
}

String String::operator+( const String& in ) const
{
  return String(_str) += in;
}

String String::operator+( const char* in ) const
{
  return String(_str) += in;
}

String& String::operator+=( const String& in )
{
  _append( in._str );
  _chars += in._chars;
  _bytes += in._bytes;
  return *this;
}

String& String::operator+=( const char* in )
{
  return *this += String(in);
}

bool String::operator<( const String& src ) const
{
  return _cmp(src._str) < 0;
}

bool String::operator==( const String& src ) const
{
  return _cmp(src._str) == 0;
}

bool String::operator==( const char* src ) const
{
  return _cmp(src) == 0;
}

bool String::operator!=( const String& src ) const
{
  return _cmp(src._str) != 0;
}

String String::left( Size chars ) const
{
  char* cur   = utf8::next8( _str, min(chars,_chars) );
  Size  bytes = cur - _str;

  char* buf   = _malloc( bytes+1 );
  _strncpy( buf, _str, bytes );
  
  String left( buf, bytes );  // bytes without 0

  _free(buf);
  return left;
}

String String::right( Size chars ) const
{
  char* cur   = utf8::prev8( _str+_bytes, _str, min(chars,_chars) );
  Size  bytes = _str + _bytes - cur;

  Size  start = _bytes - bytes;

  return String( _str+start, _bytes-start ); // bytes without 0
}

String String::mid( Size first, Size chars ) const
{
  return right( _chars-first ).left(chars);
}

int String::_cmp( const char* src ) const
{
  if( _bytes == 0 )
  {
    if( _strbytes(src) == 0 )
      return 0;
    else
      return -1;    
  }
  else
  {
    if( _strbytes(src) == 0 )
      return 1;
    else
      return strcmp(_str, src);
  }
}


void String::_copy( const char *szSrc )
{
  Size  length = _strbytes(szSrc);
  char* szDest = _malloc( length + 1 /* 0 byte */ );

  _strcpy( szDest, szSrc );
  _replace( szDest );
}

void String::_ncopy( const char *szSrc, Size length )
{
  char *szDest = _malloc( length + 1 /* 0 byte */ );

  _strncpy( szDest, szSrc, length );
  _replace( szDest );
}

char* String::_strcpy( char *szDest, const char *szSrc ) const
{
  if( szDest == 0 || szSrc == 0 )
  {
    return szDest;
  }
  return ::strcpy(szDest,szSrc);
}

char* String::_strncpy( char *szDest, const char *szSrc, Size length ) const
{
  if( szDest == 0 || szSrc == 0 )
  {
    return szDest;
  }
  ::strncpy( szDest, szSrc, length );
  szDest[length] = 0;
  return szDest;
}

void String::_append( const char *src )
{
  char* tmp = _malloc( _bytes + _strbytes(src) + 1 );
  _strcat( tmp, _str );
  _strcat( tmp, src );
  _replace( tmp );
}

char* String::_strcat( char *dest, const char* src ) const
{
  if( dest == 0 || src == 0 )
  {
    return const_cast<char*>(src);
  }  
  return ::strcat(dest,src);
}

void String::_replace( const char *szSrc )
{
  _free( _str );
  _str = const_cast<char*>(szSrc);
}

Size String::_strbytes( const char *szIn ) const
{
  if( ! szIn )
  {
    return 0;
  }
  return ::strlen(szIn);
}

char* String::_malloc( Size nbytes ) const
{
  // don't allocate memory for a negative size or
  // one of 0 or 1 byte, 1 is a "" string
  if( nbytes <= 1 )
  {
    return 0;
  }
  
  char *ptr = (char*)::malloc( nbytes );
  *ptr = 0;
  return ptr;
}

void String::_free( char *ptr ) const
{
  if( ptr )
  {
    ::free( ptr );
  }
}

} // namespace




#if 0
void CCharString::_init()
{
  _str = 0;

  if( ! _mem )
  {
    SetDefaultMalloc();
  }
}

void CCharString::_owner( char *szSrc )
{
  char *buf = 0;

  if( szSrc && _strlen(szSrc) == 0 )
  {
    _free(szSrc);
  }
  else
  {
    buf = szSrc;
  }
  _replace(buf);
}

void CCharString::_append( const char *szSrc )
{
  _str = _realloc( _str, _strlen() + _strlen(szSrc) + 1 );
  _strcat( _str, szSrc );
}

int CCharString::_cmp( const char* szSrc ) const
{
  if(_strlen() == 0)
  {
    if(_strlen(szSrc) == 0)
      return 0;
    else
      return -1;    
  }
  else
  {
    if(_strlen(szSrc) == 0)
      return 1;
    else
      return strcmp(_str, szSrc);
  }
}

size_t CCharString::_strlen() const
{
  return _strlen(_str);
}

size_t CCharString::_strlen( const char *szIn ) const
{
  if( ! szIn )
  {
    return 0;
  }
  return ::strlen(szIn);
}


char *CCharString::_strcpy( char *szDest, const char *szSrc ) const
{
  if( szDest == 0 || szSrc == 0 )
  {
    return szDest;
  }
  return ::strcpy(szDest,szSrc);
}

char *CCharString::_strncpy( char *szDest, const char *szSrc, size_t length ) const
{
  if( szDest == 0 || szSrc == 0 )
  {
    return szDest;
  }
  ::strncpy( szDest, szSrc, length );
  szDest[length] = 0;
  return szDest;
}

char *CCharString::_strcat( char *szDest, const char *szSrc ) const
{
  if( szDest == 0 || szSrc == 0 )
  {
    return szDest;
  }  
  return ::strcat(szDest,szSrc);
}

bool CCharString::_iswhite( const char cIn ) const
{
  if( cIn == 0x20 || (cIn >= 0x09 && cIn <= 0x0d) )
  {
    return true;
  }
  return false;
}
#endif

/*
String dupString( apr_pool_t* pool, const String& src )
{
  char* dup = apr_pstrmemdup( pool, src.getStr(), src.getBytes() );
  return  String( dup, src.getBytes() );
}
*/

