#include "estr.h"

#include <stdio.h>
#include <ctype.h>

#include "logger.h"
#include "eregexp.h"

#include <inttypes.h>
#include "evar.h"

#ifdef _WIN32
 #include <shlwapi.h>
#endif

#ifdef EUTILS_HAVE_ZLIB

#include "zlib.h"

#define CHUNK (1<<18)

estr gzcompress(const estr& input)
{
  estr output;

  int ret, flush;
  unsigned have;
  z_stream strm;
//  unsigned char in[CHUNK];
  unsigned char out[CHUNK];

  strm.zalloc = Z_NULL;
  strm.zfree = Z_NULL;
  strm.opaque = Z_NULL;

  // Z_DEFAULT_COMPRESSION
  ret = deflateInit2(&strm,Z_DEFAULT_COMPRESSION, Z_DEFLATED,MAX_WBITS|16,8,Z_DEFAULT_STRATEGY);
  if (ret != Z_OK){
    ldie("error initializing deflate (zlib) library");
    return(output);
  }

//    strm.avail_in = fread(in, 1, CHUNK, source);
  strm.avail_in = input.len();
//    if (ferror(source)) {
//      (void)deflateEnd(&strm);
//      return Z_ERRNO;
//    }
//    flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
  flush=Z_FINISH;
//    strm.next_in = in;
  strm.next_in = (unsigned char*)input._str;

    /* run deflate() on input until output buffer not full, finish
       compression if all of source has been read in */
  do {
    strm.avail_out = CHUNK;
    strm.next_out = out;

    ret = deflate(&strm, flush);    /* no bad return value */
    ldieif(ret == Z_STREAM_ERROR,"compressing data: Z_STREAM_ERROR");  /* state not clobbered */

    have = CHUNK - strm.avail_out;
    output+=estr((const char *)out,have);

  } while (strm.avail_out == 0);
  ldieif(strm.avail_in != 0,"not all input used");     /* all input will be used */

  /* clean up and return */
  (void)deflateEnd(&strm);
//  return Z_OK;  
  
  return(output);
}

estr gzdecompress(const estr& input)
{
    estr output;

    int ret;
    unsigned have;
    z_stream strm;
//    unsigned char in[CHUNK];
    unsigned char out[CHUNK];
    /* allocate inflate state */
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.avail_in = 0;
    strm.next_in = Z_NULL;
    ret = inflateInit2(&strm,MAX_WBITS|16);
    if (ret != Z_OK){
      ldie("failed inflateInit: "+estr(ret));
      return(output);
    }

    output.reserve(input.len()*1.5);

//        strm.avail_in = fread(in, 1, CHUNK, source);
        strm.avail_in = input.len();
//        if (ferror(source)) {
//            (void)inflateEnd(&strm);
//            return Z_ERRNO;
//        }
        if (strm.avail_in == 0)
          return(output);

//        strm.next_in = in;
        strm.next_in = (unsigned char*)input._str;
        /* run inflate() on input until output buffer not full */
        do {
            strm.avail_out = CHUNK;
            strm.next_out = out;
            ret = inflate(&strm, Z_NO_FLUSH);
            ldieif(ret == Z_STREAM_ERROR,"decompressing: Z_STREAM_ERROR");  /* state not clobbered */
            switch (ret) {
              case Z_NEED_DICT:
                ret = Z_DATA_ERROR;     /* and fall through */
              case Z_DATA_ERROR:
              case Z_MEM_ERROR:
                (void)inflateEnd(&strm);
                lerror("failed Z_MEM_ERROR or Z_DATA_ERROR: "+estr(ret)+" "+estr(Z_DATA_ERROR));
                return(output);
            }
            have = CHUNK - strm.avail_out;
            output+=estr((const char*)out,have);
//            if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
//                (void)inflateEnd(&strm);
//                return Z_ERRNO;
//            }
        } while (strm.avail_out == 0);

    /* clean up and return */
    (void)inflateEnd(&strm);
    return(output);
//    return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}


estr compress(const estr& input)
{
  estr output;

  int ret, flush;
  unsigned have;
  z_stream strm;
//  unsigned char in[CHUNK];
  unsigned char out[CHUNK];

  strm.zalloc = Z_NULL;
  strm.zfree = Z_NULL;
  strm.opaque = Z_NULL;

  // Z_DEFAULT_COMPRESSION
  ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION);
  if (ret != Z_OK){
    ldie("error initializing deflate (zlib) library");
    return(output);
  }

//    strm.avail_in = fread(in, 1, CHUNK, source);
  strm.avail_in = input.len();
//    if (ferror(source)) {
//      (void)deflateEnd(&strm);
//      return Z_ERRNO;
//    }
//    flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
  flush=Z_FINISH;
//    strm.next_in = in;
  strm.next_in = (unsigned char*)input._str;

    /* run deflate() on input until output buffer not full, finish
       compression if all of source has been read in */
  do {
    strm.avail_out = CHUNK;
    strm.next_out = out;

    ret = deflate(&strm, flush);    /* no bad return value */
    ldieif(ret == Z_STREAM_ERROR,"compressing data: Z_STREAM_ERROR");  /* state not clobbered */

    have = CHUNK - strm.avail_out;
    output+=estr((const char *)out,have);

  } while (strm.avail_out == 0);
  ldieif(strm.avail_in != 0,"not all input used");     /* all input will be used */

  /* clean up and return */
  (void)deflateEnd(&strm);
//  return Z_OK;  
  
  return(output);
}

estr decompress(const estr& input)
{
    estr output;

    int ret;
    unsigned have;
    z_stream strm;
//    unsigned char in[CHUNK];
    unsigned char out[CHUNK];
    /* allocate inflate state */
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.avail_in = 0;
    strm.next_in = Z_NULL;
    ret = inflateInit(&strm);
    if (ret != Z_OK){
      ldie("failed inflateInit: "+estr(ret));
      return(output);
    }

    output.reserve(input.len()*1.5);

//        strm.avail_in = fread(in, 1, CHUNK, source);
        strm.avail_in = input.len();
//        if (ferror(source)) {
//            (void)inflateEnd(&strm);
//            return Z_ERRNO;
//        }
        if (strm.avail_in == 0)
          return(output);

//        strm.next_in = in;
        strm.next_in = (unsigned char*)input._str;
        /* run inflate() on input until output buffer not full */
        do {
            strm.avail_out = CHUNK;
            strm.next_out = out;
            ret = inflate(&strm, Z_NO_FLUSH);
            ldieif(ret == Z_STREAM_ERROR,"decompressing: Z_STREAM_ERROR");  /* state not clobbered */
            switch (ret) {
              case Z_NEED_DICT:
                ret = Z_DATA_ERROR;     /* and fall through */
              case Z_DATA_ERROR:
              case Z_MEM_ERROR:
                (void)inflateEnd(&strm);
                lerror("failed Z_MEM_ERROR or Z_DATA_ERROR: "+estr(ret)+" "+estr(Z_DATA_ERROR));
                return(output);
            }
            have = CHUNK - strm.avail_out;
            output+=estr((const char*)out,have);
//            if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
//                (void)inflateEnd(&strm);
//                return Z_ERRNO;
//            }
        } while (strm.avail_out == 0);

    /* clean up and return */
    (void)inflateEnd(&strm);
    return(output);
//    return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}
#endif

estr json_escape_string(const estr& str)
{
  estr res(str);
  res.replace("\\","\\\\");
  res.replace("\"","\\\"");
  res.replace("\n","\\n");
  res.replace("\r","\\r");
  res.replace("\t","\\t");
  return(res);
}
void estr::json_serial(estr& data) const
{
  data.reserve(data.len()+len()+2);
  data+="\"";
  data+=json_escape_string(*this);
  data+="\"";
}

void estr::serial(const efile& f) const
{
  serialuint(len(),f);
  f.write(_str,len());
}
void estr::serial(estr& data) const
{
  data.reserve(data.len()+sizeof(uint32_t)+len());
  *((uint32_t*)&data[data._strlen])=len();
  memcpy(&data[data._strlen+sizeof(uint32_t)],_str,len());
  data._strlen+=len()+sizeof(uint32_t);
}
bool estr::unserial(const efile& f)
{
  unsigned int len;
  if (!unserialuint(len,f)) return(false);
  reserve(len);

  long l=f.read(_str,len);
  if (l!=len) return(false);

  _str[len]=0x00;
  _strlen=len;
  return(true);
}

size_t estr::unserial(const estr& data,size_t i)
{
  if (i==-1) return(i);

  unsigned long len;
  if (data.len()<i+sizeof(uint32_t)) return(-1);
  len=*((uint32_t*)&data._str[i]);
//  if (data.len()<i+len){ lerror("truncated serial string? i: "+estr(i)+" len: "+len+" data.len: "+data.len()); return(-1); }
  if (data.len()<i+sizeof(uint32_t)+len) return(-1);

  _checkNewSize(len);
  memcpy(_str,&data._str[i+sizeof(uint32_t)],len);
  _str[len]=0x00;
  _strlen=len;
  return(i+len+sizeof(uint32_t));
}

bool estr::is_hex() const
{
  if (len()<3) return(false);


  if (_str[0]!='0' || _str[1]!='x') return(false);

  long int i;
  for (i=2; i<len(); ++i){
    if ((_str[i] < '0' || _str[i] > '9') && (_str[i]<'a' || _str[i]>'f') && (_str[i]<'A' || _str[i]>'F')) return(false);
  }
  return(true);
}

bool estr::is_int() const
{
  if (!len()) return(false);

  long int i;
  i=0;
  if (_str[0]=='-' && i+1 < len())
    i=1;
  for (; i<len(); ++i){
    if (_str[i] < '0' || _str[i] > '9') return(false);
  }
  return(true);
}

bool estr::is_float() const
{
  if (!len()) return(false);

  long int i;
  i=0;
  if (_str[0]=='-' && i+1 < len())
    i=1;
  if (strncmp(&_str[i],"inf",_strlen-i)==0 || strncmp(&_str[i],"nan",_strlen-i)==0) return(true);
  bool exp;
  bool dot;
  bool num;
  num=false;
  exp=false;
  dot=false;
  for (; i<len(); ++i){
    if (_str[i] < '0' || _str[i] > '9'){
      if (!num || exp)
        return(false);
      if (_str[i]=='e' || _str[i]=='E'){
        exp=true;
        if (i+1<len()){
          if (_str[i+1]=='-' || _str[i+1]=='+')
            ++i;
        }else
          return(false);   // exponent letter appearing without exponent number
      }else if (_str[i]=='.' && !dot){
        dot=true;
      }else{
        return(false);
      }
    }else
      num=true;
  }
  return(true);
}

bool estr::is_num() const
{
  return(is_int() || is_float());
}

int estr::h() const
{
  int i=0;
  if (!sscanf(_str,"%x",&i)) i=0;
  return(i);
}

int estr::i() const
{
  int i=0;
  if (!sscanf(_str,"%i",&i)) i=0;
  return(i);
}

long estr::l() const
{
  long i=0l;
  if (!sscanf(_str,"%li",&i)) i=0l;
  return(i);
}

float estr::f() const
{
  float f=0.0;
  if (!sscanf(_str,"%g",&f)) f=0.0;
  return(f);
}

double estr::d() const
{
  double d=0.0l;
  if (!sscanf(_str,"%lg",&d)) d=0.0;
  return(d);
}

estr& estr::uppercase()
{
  char *p;
  p=_str;
  while(*p!=0x00){
    *p = toupper(*p); ++p;
  }
  return(*this);
}

estr& estr::lowercase()
{
  char *p;
  p=_str;
  while(*p!=0x00){
    *p = tolower(*p); ++p;
  }
  return(*this);
}

estr estr::hex() const
{
  estr res;
  res.reserve(len()*2+2);
  res+="0x";
  char hexstr[3];
  for (long i=0; i<len(); ++i){
    ::sprintf(hexstr,"%.2hhx",_str[i]);
    res += hexstr;
  }
  return(res);
}

unsigned char chex2bin(char c){
  if (c >= '0' && c <= '9')
    return(c-'0');
  else if (c >= 'a' && c <= 'f')
    return(10u+c-'a');
  else if (c >= 'A' && c <= 'F')
    return(10u+c-'A');
  ldie("error converting hex to decimal: "+estr(c));
  return(0);
}

estr estr::hex2bin() const
{
  estr res;
  res.reserve(len()/2+len()%2);
  char bchr;
  long i;
  for (i=0; i+1<len(); i+=2){
    bchr=static_cast<char>((chex2bin(_str[i])<<4)|chex2bin(_str[i+1]));
    res += bchr;
  }
  if (i<len())
    res += static_cast<char>(chex2bin(_str[i])<<4);
  return(res);
}




//eregexp re_endln("\r?\n");

estr estr::getline()
{
  long int i,i2;
  estr ptmp;
//  estr res;
  const char *echar="\n";

//  i=re_strpos(*this,re_endln);
  i=find(echar);
  if (i==-1){
    ptmp=*this;
    clear();
    return(ptmp);
  }

  ptmp=substr(0,i);
  if (_str[i]==0x0C){
    _str[i]=0x00;
    del(0,i+2); // if we have \r\n remove both, else
  } else {
    _str[i]=0x00;
    del(0,i+1); // just remove \n
  }
  return(ptmp);
}

const char *estr::getline(long int &l,const char *echar)
{
  long int i,i2;
  char *ptmp;
//  estr res;

  ptmp=_str;
//  i=re_strpos(*this,re_endln);
  i=find(echar);

  if (i==-1){
//    res=*this;
    l=len();
    del(0,len());
    return(ptmp);
  }

//  res=substr(0,i);
  l=i;
  if (_str[i]==0x0C){
    _str[i]=0x00;
    del(0,i+2); // if we have \r\n remove both, else
  } else {
    _str[i]=0x00;
    del(0,i+1); // just remove \n
  }
  

  return(ptmp);
}

bool estr::getline(estr& line)
{
  line.clear();
  long int i;

  i=find('\n');
  if (i==-1){
    if (len()==0)
      return(false);
    line=*this;
    clear();
    return(true);
  }

  if (i>0 && _str[i-1]=='\r')
    line=substr(0,i-1);
  else
    line=substr(0,i);
  del(0,i+1);

  return(true);
}

/*
const char *estr::getline()
{
  long int i,i2;
  char *ptmp;

  ptmp=_str;
  i=find('\n');
  if (i==-1)
    return(NULL);

  if (i>0 && _str[i-1]==0x0D){
    _str[i-1]=0x00;
    del(0,i+1); // if we have \r\n remove both, else
  } else {
    _str[i]=0x00;
    del(0,i+1); // just remove \n
  }

  return(ptmp);
}
*/

/*
  estr res;
  int i,k;

  k=0;
  i=re.match(str);
  while (i!=-1 && i<str.len()){
    res += str.substr(k,i-k);
    res += replace;
//    cout << str.substr(k,i-k) << endl;
//    cout << "re.b: "<< re.b <<" ,re.e: "<<re.e<<endl;
    k=re.e;
    i=re.match(str,re.e);
  }
  res += str.substr(k);
  return(res);
*/
estr &estr::replace(const estr &pattern,const estr &replace)
{
  estr res;
  long int i,k;

  k=0;
  i=find(pattern);
  while (i!=-1 && i<len()){
    res += substr(k,i-k);
    res += replace;
//    cout << str.substr(k,i-k) << endl;
//    cout << "re.b: "<< re.b <<" ,re.e: "<<re.e<<endl;
    k=i+pattern.len();
    i=find(pattern,k);
  }
  res += substr(k);
  *this=res;
  return(*this);
}

estr& estr::trim()
{
  char *pd,*ps,*p2;
  bool match;
  bool lastspace;

  lastspace=true;
  pd=_str; ps=_str;
  while(*ps!=0x00){
    match=false;
    if (lastspace){
       if (*ps == ' ') match=true;
       else lastspace=false;
    }
    else if (*ps == ' ') lastspace=true;
    if (!match){
      *pd=*ps;
      ++ps; ++pd;
    }
    else
      ++ps;
  }
  if (lastspace && pd != _str) --pd;
  *pd=*ps;
  _strlen = strlen(_str);
  return(*this);
}

void estr::delstr(const char *str)
{
  char *pd,*ps;
  const char *ps2,*p2;
  long int slen;
  bool match;

  slen=strlen(str);
  pd=_str; ps=_str;
  while(*ps!=0x00){
    match=true;
    ps2=ps;
    for (p2=str,ps2=ps; *p2!=0x00 && *ps2!=0x00; ++p2,++ps2){
      if (*ps2 != *p2)
        { match=false; break; }
    }
    if (!match){
      *pd=*ps;
      ++ps; ++pd;
    }
    else
      ps+=slen;
  }
  *pd=*ps;
  _strlen = strlen(_str);
}

estr strprintf(estr format,...)
{
  estr r;
  va_list ap;
  char tmpsz[1024];

  va_start(ap,format);

  vsnprintf(tmpsz,1024,format._str,ap);
  r = tmpsz;
  va_end(ap);
  return(r);
}


estr& estr::sprintf(estr format,...)
{
  va_list ap;
  char tmpsz[1024];

  va_start(ap,format);

  vsnprintf(tmpsz,1024,format._str,ap);
  *this = tmpsz;

  va_end(ap);
  return(*this);
}

void estr::remove_chars(const char *remove_chars)
{
  char *pd,*ps;
  const char *p2;
  bool match;

  pd=_str; ps=_str;
  while(*ps!=0x00){
    match=false;
    for (p2=remove_chars; *p2!=0x00; ++p2){
      if (*ps == *p2)
        { match=true; break; }
    }
    if (!match){
      *pd=*ps;
      ++ps; ++pd;
    }
    else
      ++ps;
  }
  *pd=*ps;
  _strlen = strlen(_str);
}

void estr::clean(const char *ignore_chars)
{
  char *p;
  const char *p2;
  for(p=_str; *p!=0x00; ++p){
    for (p2=ignore_chars; *p2!=0x00; ++p2){
      if (*p == *p2)
        { *p = ' '; break; }
    }
  }
}

estrarray estr::explode(const estr &sep) const
{
  estrarray res;
  long int i,k;
  if (len()==0) return(res);

  k=0;
  i=find(sep);
  while (i!=-1 && i<len()){
    res.add(substr(k,i-k));
    k=i+sep.len(); 
    i=find(sep,i+1);
  }
  
  if (len())
    res.add(substr(k));

  return(res);
}

estrarray estr::grep(const estr& str,const estr& sep) const
{
  return(explode(sep).grep(str));
}

#ifdef EUTILS_HAVE_REGEX
estrarray estr::egrep(const eregexp& re,const estr& sep) const
{
  return(explode(sep).egrep(re));
}

estrarray estr::egrepo(const eregexp& re,const estr& sep) const
{
  return(explode(sep).egrepo(re));
}
#endif

void estr::_checkNewSize(long int size)
{
  if (size >= _asize){
    delete[] _astr;
//    _asize = (size/255+1)*255;
    _asize = size+1;
    _astr = new char[_asize];
    _str = _astr;
  }
}

void estr::_checkSize(long int size)
{
  char *tmpsz;
//  printf(" warning: asize %i  size %i  >=  %i     d: %i\n",_asize,size,_asize - (_str-_astr),(_str-_astr));
  if (size >= _asize){
    _asize = (size/255+1)*255;
    tmpsz = new char[_asize];
    memcpy(tmpsz,_str,_strlen+1);
    delete[] _astr;
    _astr = tmpsz;
    _str = _astr;
  }
  else if (size >= _asize - (_str - _astr)){
    // _str pointer is too much forward in _astr, that we have to copy it back
    memmove(_astr,_str,_strlen+1);
    _str=_astr;
  }

}

estr &estr::operator =(const evar &var)
{
  if (var.getTypeid()==typeid(estr))
    return(*this=var.get<estr>());

  if (var.isConvertible(typeid(estr))){
    evar cvar(var.convert(typeid(estr)));
    return(*this=cvar.get<estr>());
  }
  *this=estr("<")+var.getClass()+">";
//  lerror("unable to convert evar of type: "+estr(var.getClass())+" to estr");
  return(*this);
}

estr &estr::operator =(const estr &str)
{
  if (&str==this) return(*this); // avoid copying yourself

  _checkNewSize(str.len());
  _str=_astr;
  memcpy(_str,str._str,str.len()+1);
  _strlen = str.len();
  return(*this);
}

estr &estr::operator+=(const estr &str)
{
  long int slen;
  slen=str.len()+_strlen;
  _checkSize(slen);
  memcpy(&_str[len()],str._str,str.len()+1);
  _strlen = slen;
  return(*this);
}

estr  estr::operator+ (const estr &str) const
{
  estr str2;
  str2=*this;
  str2+=str;
  return(str2);
}

estr &estr::operator+=(const char *str)
{
  long int slen;
  slen = strlen(str)+_strlen;
  _checkSize(slen);
  memcpy(&_str[len()],str,strlen(str)+1);
  _strlen = slen;
  return(*this);
}

estr &estr::operator=(const char *str)
{
  long int slen;
  if (str==0x00) { clear(); return(*this); }
  slen = strlen(str);
  _checkNewSize(slen);
  _str=_astr;
  strcpy(_str,str);
  _strlen = slen;
  return(*this);
}

#ifdef _WIN32
estr &estr::operator=(const wchar_t *str)
{
	long int slen;
	slen = wcslen(str);
	reserve(slen);
	wcstombs(_str, str, slen);
	_str[slen] = 0x00;
	_strlen = slen;
	return(*this);
}

void wstr(wchar_t **lpcwstr,const estr& str)
{
  *lpcwstr = new wchar_t[str._strlen * 2];
  mbstowcs(*lpcwstr, str._str, str._strlen*2);
}
#endif

estr estr::operator+(const char *str) const
{
  estr str2(*this);
  str2+=str;
  return(str2);
}

void estr::insert(long int pos,char c)
{
  ldwarnif(pos>_strlen,"estr::insert, out of bounds");

  _checkSize(_strlen+1);
  
  long int i;
  for (i=_strlen; i>=pos; --i)
    _str[i+1] = _str[i];

  _str[pos] = c;
  ++_strlen;
}

void estr::insert(long int pos,const char *str)
{
  ldwarnif(pos>_strlen,"estr::insert, out of bounds");

  long int slen;

  slen = strlen(str);
  _checkSize(_strlen+slen);
  
  long int i;
  for (i=_strlen; i>=pos; --i)
    _str[i+slen] = _str[i];

  for (i=0; i<slen; ++i)
    _str[pos+i] = str[i];
  _strlen+=slen;
}

void estr::append(const char *str,long int slen)
{
  _checkSize(_strlen+slen);
  memcpy(_str+_strlen,str,slen);
  _strlen+=slen;
  _str[_strlen]=0x00;
}

long int estr::erase(long int i,long int l)
{
  if (i<0) i+=len();
  if (l<0) l+=len()-i+1;
 
  if (i>=len() || i<0 || l<=0) return(0);

  if (i+l>len()) l=len()-i;
  
  long int d;

  if (i==0){
    _str = &_str[l];
//    printf(" d: %i\n ",_str-_astr);
  }else{
    d=len()-i-l+1;
    for (; d>0; --d,++i)
      _str[i]=_str[i+l];
  }

  _strlen -= l;
  return(l); 
}

estr estr::substr(long int i,long int l) const
{
  estr res;

  if (i<0) i+=len();    // if i is negative then assume its counting from the end
//  if (l<0) l+=len()-i; // if len = -1, copy till the end of the file
  if (l<0) l+=len()-i+1; // if len = -1, copy till the end of the file

  if (i>=len() || i<0 || l<=0) return(res);

  l+=i;
  if (l>len()) l=len();

  res._checkNewSize(l-i);
  memcpy(res._str,&_str[i],l-i);
  res._str[l-i]=0x00;
  res._strlen=l-i;
//  for (;i<l;++i)
//    res+=_str[i];

  return(res);
}

long estr::findchr(const estr& chrs,long i) const
{
  if (!_str || i>=_strlen) return(-1l);
  for (char *pd=&_str[i]; pd<_str+_strlen; ++pd){
    for (long j=0; j<chrs.len(); ++j){
      if (*pd==chrs[j]) return(pd-_str);
    }
  }
  return(-1l);
}

long int estr::ifind(const estr &needle,long int i) const
{
  char *r;
  if (needle.len()==0) { lwarn("estr::find: needle string is empty!"); return(-1); }

  if (i<0) i+=len();
  if (i>=len() || i<0) return(-1);

  r=strcasestr(&_str[i],needle._str);

  if (!r) return(-1);

  return(r-_str);
}

long int estr::find(const estr &needle,long int i) const
{
  char *r;
  if (needle.len()==0) { lwarn("estr::find: needle string is empty!"); return(-1); }

  if (i<0) i+=len();
  if (i>=len() || i<0) return(-1);

  r=strstr(&_str[i],needle._str);

  if (!r) return(-1);

  return(r-_str);
}

long int estr::count(const estr &needle,long int i) const
{
  long int count;

  lddieif(i<0 || i >= len(),"estr::count index out of bounds");
  count=-1;
  --i;
  do {
    i=find(needle,i+1);
     ++count; 
  }while(i!=-1 && i<len());
  return(count);
}



void estr::init(long s)
{
  _asize = s+1;
  _strlen = 0;
  _astr = new char[_asize];
  _str = _astr;
  _str[0] = 0x00;
}


#ifdef __APPLE__
estr::estr(CFStringRef cfstr)
{
  init();
  *this=cfstr;
}

estr& estr::operator=(CFStringRef cfstr)
{
  const char *str;
  str=CFStringGetCStringPtr(cfstr,kCFStringEncodingMacRoman);
  if (str==NULL){
    reserve(CFStringGetLength(cfstr));
    lerrorif(!CFStringGetCString(cfstr,_str,_asize,kCFStringEncodingMacRoman),"did not reserve enough space for CFString");
  }
  *this=str;
  return(*this);
}
#endif


estr::estr():_str(0x00),_astr(0x00)
{
  init(0);
}


estr::estr(bool v):_str(0x00),_astr(0x00)
{
  init();
  *this=(v?"true":"false");
}

estr::estr(const unsigned long int i):_str(0x00),_astr(0x00)
{
  init();
  ::sprintf(_str,"%lu",i);
  _strlen = strlen(_str);
}

estr::estr(const unsigned int i):_str(0x00),_astr(0x00)
{
  init();
  ::sprintf(_str,"%u",i);
  _strlen = strlen(_str);
}

estr::estr(const long int i):_str(0x00),_astr(0x00)
{
  init();
  ::sprintf(_str,"%li",i);
  _strlen = strlen(_str);
}

estr::estr(const int i):_str(0x00),_astr(0x00)
{
  init();
  ::sprintf(_str,"%i",i);
  _strlen = strlen(_str);
}

estr::estr(const float f):_str(0x00),_astr(0x00)
{
  init();
  ::sprintf(_str,"%.8g",f);
  _strlen = strlen(_str);
}

estr::estr(const double d):_str(0x00),_astr(0x00)
{
  init();
  ::sprintf(_str,"%g",d);
  _strlen = strlen(_str);
}

estr::estr(const char c):_str(0x00),_astr(0x00)
{
  init(1);
  _str[0] = c;
  _str[1] = 0x00;
  _strlen = 1;
}

estr::estr(const unsigned char c):_str(0x00),_astr(0x00)
{
  init(1);
  _str[0] = c;
  _str[1] = 0x00;
  _strlen = 1;
}

estr::estr(void *p):_str(0x00),_astr(0x00)
{
  init();
  ::sprintf(_str,"%lx",(long unsigned)p);
  _strlen = strlen(_str);
}

estr::estr(const evar &var):_str(0x00),_astr(0x00)
{
  init();
  *this = var;  
}

estr::estr(const estr &str):_str(0x00),_astr(0x00)
{
  init(str.len());
  *this = str;  
}

estr::estr(const char *str):_str(0x00),_astr(0x00)
{
  init(strlen(str));
  *this = str;  
}

estr::estr(const char *str,int len):_str(0x00),_astr(0x00)
{
  init(len);
  memcpy(_str,str,len);
//  strncpy(_str,str,len); // cannot use strncpy because it will not work with binary strings (strncpy will stop prematurely on a null terminated character)
  _str[len]=0x00;
  _strlen=len;
}

estr::estr(const std::string& str):_str(0x00),_astr(0x00)
{
  init(str.length());
  memcpy(_str,str.c_str(),str.length());
//  strncpy(_str,str.c_str(),str.length());
  _str[str.length()]=0x00;
  _strlen=str.length();
}


#ifdef _WIN32
estr::estr(const wchar_t* str): _str(0x00),_astr(0x00)
{
  init();
  *this = str;
}
#endif

estr::estr(const estrarray &strarr):_str(0x00),_astr(0x00)
{
  init();

  if (strarr.size()){  
    int i;
    for (i=0; i<strarr.size()-1; ++i){
      if (strarr.keys(i).len()){
         *this += strarr.keys(i);
         *this += "=";
      }
      *this += strarr.values(i);
      *this += ",";
    }
    if (strarr.size()){
      if (strarr.keys(i).len()){
        *this += strarr.keys(i);
        *this += "=";
      }
      *this += strarr.values(i);
    }
  }
}



estr::~estr()
{
  if (_astr)
    delete[] _astr;
}

