#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include "myc_err.h"
#include "myc_str.h"
#include "myc_buf.h"

char *StrNGet(StrBuf *buf, char *result, int reslen, int sep) {
  char *b, *f, *e, quote;
  int l;

  if (buf->rdpos < 0 || buf->rdpos >= buf->dsize || buf->buf==NULL)
      ERR_MSG("buffer corrupt");
  b=buf->buf + buf->rdpos;
  if (*b=='"' || *b=='\'') { /* take string within quotes (single or double) */
    quote=*b; b++;
    f=strchr(b, quote);
    if (f==NULL)
        ERR_MSG("missing '""'");
    l=f-b;
    if (sep == StrNONE) {
      buf->rdpos=f - buf->buf + 1;
      buf->seen = 0;
    } else {
      e=strchr(f+1, sep);
      if (e==NULL) {
        buf->rdpos = f - buf->buf + 1 + strlen(f+1);
        buf->seen=0;
      } else {
        buf->rdpos = e - buf->buf + 1;
        buf->seen=1;
      }
    }
  } else {
    f=strchr(b, sep);
    if (f==NULL) {
      l=strlen(b);
      f=b+l;
      buf->rdpos+=l;
      buf->seen=0;
    } else {
      l=f-b;
      buf->rdpos+=l+1;
      buf->seen=1;
    }
  }
  if (result==NULL) {
    if (reslen==-1) { /* special case for StrGetBuf */
      return(f);
    }
    *f='\0';
    return(b);
  } else {
    if (l>=reslen) l=reslen-1; /* ERR_MSG("result too short"); */
    strncpy(result, b, l);
    result[l]='\0';
    return(result);
  }
  OnError:
    buf->rdpos=buf->dsize; /* illegal value */
    return(NULL);
}

int StrGetBuf(StrBuf *buf, StrBuf *res, int sep) {
  char *b, *f;

  b = buf->buf + buf->rdpos;
  ERR_P(f=StrNGet(buf, NULL, -1, sep));
  res->buf   = b;
  res->wrpos = f-b;
  res->rdpos = 0;
  res->dsize = f-b;
  return(0);
  OnError:
    buf->rdpos=buf->dsize; /* illegal value */
    return(-1);
}

int StrGetInt(StrBuf *buf, int *res, int sep) {
  char num[32];
  int i, l, p;

  p=buf->rdpos;
  ERR_P(StrGet(buf, num, sep));
  i=sscanf(num, "%d%n", res, &l);
  if (i==0)
      ERR_MSG("illegal number");
  if (sep==StrNONE) {
    buf->rdpos=p+l;
  }
  return(0);
  OnError:
    buf->rdpos=buf->dsize; /* illegal value */
    return(0);
}

int StrGetFloat(StrBuf *buf, float *res, int sep) {
  char num[32];
  int i, l, p;

  p=buf->rdpos;
  ERR_P(StrGet(buf, num, sep));
  if (num[0]=='N' && num[1]=='a' && num[2]=='N') {
    l=3;
    *res = MYC_NAN;
  } else {
    i=sscanf(num, "%f%n", res, &l);
    if (i==0) {
      ERR_MSG("illegal number");
    }
  }
  if (sep==StrNONE) {
    buf->rdpos=p+l;
  }
  return(0);
  OnError:
    buf->rdpos=buf->dsize; /* illegal value */
    return(-1);
}

int StrGetEnd(StrBuf *buf) {
  if (buf->buf!=NULL) {
    if (buf->rdpos < 0 || buf->rdpos >= buf->dsize)
        ERR_MSG("buffer corrupt");
    if (buf->rdpos != buf->wrpos)
        ERR_MSG("superflous content in buffer");
  }
  return(0);
  OnError:
    return(-1);
}

int StrPut(StrBuf *buf, const char *str, int sep) {
  int l, pos;
  char quote;

  pos=buf->wrpos;
  if (pos < 0 || pos >= buf->dsize || buf->buf==NULL)
      ERR_MSG("buffer corrupt");
  l=strlen(str);
  quote='\0';
  if (sep>0) {
    if (strchr(str, sep)!=NULL) {
      if (strchr(str, '"')!=NULL) {
        quote='"'; l+=2;
      }
      if (strchr(str, '\'')!=NULL) {
        if (quote!='\0')
            ERR_MSG("str must not contain separator and both kind of quotes");
        quote='\''; l+=2;
      }
    }
  }
  if (pos+l >= buf->dsize) {
    ERR_MSG("buffer too short");
  }
  if (quote!='\0') {
    buf->buf[pos]=quote; pos++;
    strcpy(buf->buf + pos, str);
    buf->buf[pos]=quote; pos++;
  } else {
    strcpy(buf->buf + pos, str);
  }
  pos+=l;
  if (sep!=StrNONE) {
    buf->buf[pos]=sep;
    pos++;
  } else {
    buf->buf[pos]='\0';    
  }
  buf->wrpos=pos;
  return(0);
  OnError:
    buf->wrpos=-1;
    return(-1);
}

int StrPutInt(StrBuf *buf, int val, int sep) {
  char num[32];

  sprintf(num, "%d", val);
  ERR_I(StrPut(buf, num, sep));
  return(0);
  OnError:
    return(-1);
}

int StrPutFloat(StrBuf *buf, float val, int sep) {
  char num[32];

  if (val == MYC_NAN) {
    ERR_I(StrPut(buf, "NaN", sep));
  } else {
    sprintf(num, "%g", val);
    ERR_I(StrPut(buf, num, sep));
  }
  return(0);
  OnError:
    return(-1);
}

void StrReset(StrBuf *buf) {
  buf->rdpos=0;
}

void StrClear(StrBuf *buf) {
  buf->rdpos=0;
  buf->wrpos=0;
}

void StrNLink(StrBuf *buf, char *str, int length) {
  int l;
  buf->buf=str;
  buf->rdpos=0;
  l=strlen(str);
  if (l<length) l=length;
  buf->wrpos=l;
  buf->dsize=buf->wrpos;
}

#define TWO_23 8388608
#define EXP_OFFS 128

void flt_to_char4(double f, char buf[4]) {
  double m;
  int e;
  long res;

  m = frexp(f, &e);
  e += EXP_OFFS;
  if (e < 0 || m == 0) {
    res = 0; m = 0;
  } else {
    if (e > 255) {
      res = 255 * TWO_23 + (TWO_23 - 1); /* max. representable number */
    } else {
      res = (long)(0.5 + (fabs(m * 2) - 1.0) * TWO_23);
      /* here we may think about the case, where m is just a little less than 1.0:
         the mantissa part overflows to bit23, but, the result would be o.k., since this
         just increases the exponent by one, and the mantissa bits get all zero.
         but what happens when e is already 255 ? the next line handles this */
      if (res == TWO_23 && e == 255) res--;
      res += e * TWO_23;
    }
  }
  buf[0] = res % 256; res = res / 256;
  buf[1] = res % 256; res = res / 256;
  buf[2] = res % 256; res = res / 256;
  if (m < 0) {
    buf[3] = res - 128;
  } else {
    buf[3] = res;
  }
}

double flt_from_char4(char buf[4]) {
  long i, b0, b1, b2, b3;
  
  b0=buf[0]; if (b0<0) b0+=256;
  b1=buf[1]; if (b1<0) b1+=256;
  b2=buf[2]; if (b2<0) b2+=256;
  b3=buf[3]; if (b3<0) b3+=256;
  if (b3>=128) {
    i=(b3-128)*(256*65536)+b2*65536+b1*256+b0;
    if (i==0) return 0.0;
    return -ldexp((i % TWO_23)*1.0/(TWO_23)*0.5+0.5, i/TWO_23-EXP_OFFS);
  } else {
    i=b3*(256*65536)+b2*65536+b1*256+b0;
    if (i==0) return 0.0;
    return ldexp((i % TWO_23)*1.0/(TWO_23)*0.5+0.5, i/TWO_23-EXP_OFFS);
  }
}

int StrPutArray(StrBuf *buf, float val[], int size) {
  int i, pos;
  char *b;

  pos=buf->wrpos;
  if (pos < 0 || pos >= buf->dsize || buf->buf==NULL)
      ERR_MSG("buffer corrupt");
  if (pos+4*size >= buf->dsize)
      ERR_MSG("buffer too short");
  b=buf->buf+pos;
  flt_to_char4((float)size, b);
  b+=4;
  for (i=0; i<size; i++) {
    flt_to_char4(val[i], b);
    b+=4;
  }
  buf->wrpos=b - buf->buf;
  return(0);
  OnError:
    buf->wrpos=-1;
    return(-1);
}

int StrGetArray(StrBuf *buf, float val[], int maxsize) {
  int size, i;
  char *b;
  double gg;

  if (buf->rdpos < 0 || buf->rdpos >= buf->dsize || buf->buf==NULL)
      ERR_MSG("buffer corrupt");
  b=buf->buf + buf->rdpos;
  size=flt_from_char4(b);  b+=4;
  buf->rdpos+=4*(size+1);
  if (maxsize<size) size=maxsize;
  for (i=0; i<size; i++) {
    gg=flt_from_char4(b); b+=4;
    val[i]=gg;
  }
  return size;
  OnError:
    buf->rdpos=buf->dsize; /* illegal value */
    return(-1);
}
