
/* switch off ANSI_C_SOURCE (allows compiling all sources with cc/stand=ansi) */
#ifdef __VMS
#ifdef __HIDE_FORBIDDEN_NAMES
#undef __HIDE_FORBIDDEN_NAMES
#endif
#endif

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdio.h>
#include <assert.h>
#include "errhdl.h"
#include "util.h"
#include "dlog.h"

#define VERSION 1.1

#define DEFINED(F) (abs((F)-dset->undef)>1.0e-5*(F))

int DlogWrite(DlogSet *dset, int idx, void *data) {
  int p;

  p = dset->headsize + (idx % dset->nlen) * dset->fsize;
  if (p!=dset->pos) {
    ERR_SI(lseek(dset->fd, p, SEEK_SET));
  }
  ERR_SI(write(dset->fd, data, dset->fsize));
  dset->pos = p + dset->fsize;
  return(0);
  OnError:
    return(-1);
}

int DlogRead(DlogSet *dset, int idx, void *data) {
  int p;

  p = dset->headsize + (idx % dset->nlen) * dset->fsize;
  if (p!=dset->pos) {
    ERR_SI(lseek(dset->fd, p, SEEK_SET));
  }
  ERR_SI(read(dset->fd, data, dset->fsize));
  dset->pos = p + dset->fsize;
  return(0);
  OnError:
    return(-1);
}

int DlogOpen(DlogSet *dset, char *name, int write) {
  int i, p, np, fd;

  fd=0;
  str_copy(dset->name, name);
  if (write) {
#ifdef __VMS
    ERR_SI(fd=open(name, O_RDWR | O_SYNC, 0, "SHR=UPD"));
#else
    ERR_SI(fd=open(name, O_RDWR | O_SYNC, 0));
#endif
  } else {
#ifdef __VMS
    ERR_SI(fd=open(name, O_RDONLY | O_SYNC, 0, "SHR=UPD"));
#else
    ERR_SI(fd=open(name, O_RDONLY | O_SYNC, 0));
#endif
  }
  dset->fd=fd;
  
  p=(char *)&dset->fd - (char *)dset;
  ERR_SI(read(dset->fd, dset, p));
  dset->pos=p;
  if ((int)(dset->version*1000+0.5) != (int)(VERSION*1000+0.5)) ERR_MSG("version mismatch");
  if (dset->headsize != p) ERR_MSG("illegal dlog file");
  return(0);
  OnError:
    if (fd!=0) close(fd);
    return(-1);
}

int DlogCreate(DlogSet *dset, char *name, time_t start, int nset, int nlen, int period, float undef) {
  int fd, i, j, p;
  va_list ap;
  float f[DLOG_MAX_SET];

  fd=0;
  if (nset>DLOG_MAX_SET) ERR_MSG("nset too large");
  dset->nset=nset;
  dset->nlen=nlen;
  dset->start=start;
  dset->last=start-period;
  dset->period=period;
  dset->undef=undef;
  dset->version=VERSION;
  str_copy(dset->name, name);
  
  for (i=0; i<nset; i++) {
    f[i]=dset->undef;
  }
  ERR_SI(fd=open(name, O_RDWR | O_CREAT | O_SYNC | O_TRUNC, 0666));
  dset->fd=fd;
  p=(char *)&dset->fd - (char *)dset;
  dset->headsize = p;
  dset->fsize = nset * sizeof(float);
  ERR_SI(write(dset->fd, dset, p));
  dset->pos=p;
  return(0);
  OnError:
    if (fd!=0) close(fd);
    return(-1);
}

int DlogPut(DlogSet *dset, time_t time, int nset, float val[]) {
  int i, i0, j;
  float f[DLOG_MAX_SET], f0[DLOG_MAX_SET];

  if (nset > dset->nset) nset = dset->nset;

  for (j=0; j < nset; j++) { f[j]=val[j]; }
  for (j=nset; j < dset->nset; j++) { f[j]=dset->undef; }

  i = ( time - dset->start ) / dset->period;
  i0 = ( dset->last - dset->start ) / dset->period;
  
  if (i0==i) {
    ERR_I(DlogRead(dset, i, f0));
    for (j=0; j<nset; j++) {
      if (DEFINED(f0[j]) && /* when last point defined */
         (i%2 && f0[j]>f[j] || /* take maximum (for odd i) */
         !i%2 && f0[j]<f[j] )  /* or minimum (for even i) */
        ) f[j]=f0[j];          /* of f0 and f0 */
    }
  } else if (i0<i-1) {
    for (j=0; j < dset->nset; j++) { f0[j]=dset->undef; }
    i0++;
    while (i0<i) {
      ERR_I(DlogWrite(dset, i0, f0));
      i0++;
    }
  }
  ERR_I(DlogWrite(dset, i, f));
  dset->last=time;
  return(0);
  OnError:
    return(-1);
}

int DlogGet(DlogSet *dset, int iset, int nmax, double *starttime, float x[], float y[]) {
  int i0, i1, i, n, undef;
  float f[DLOG_MAX_SET];

  if (iset<0) ERR_MSG("illegal iset");
  if (iset>=dset->nset) return(0);

  i = ( dset->last - dset->start ) / dset->period;
  if (i - dset->nlen > 0) {
    i1 = i - dset->nlen + 1;
  } else {
    i1 = 0;
  }
  i0=i1;

  *starttime = dset->start + i1 * dset->period;
  n=0;
  undef=2;
  while (i0<=i) {
    if (n>=nmax) return(n);
    ERR_I(DlogRead(dset, i0, f));
    i0++;
    if (DEFINED(f[iset])) {
      x[n]=(float)(i0-i1)*dset->period;
      y[n]=f[iset];
      n++;
      undef=0;
    } else if (undef==0) {
      undef=1;
    } else if (undef==1) {
      undef=2;
      x[n]=0;
      y[n]=0;
      n++;
    }
  }  
  return(n);
  OnError:
    return(-1);
}

int DlogGetMany(DlogSet *dset, int nset, int nmax, double *starttime, float x[], float y[], int index[]) {
  int n, k, nmx, ntot;

  ntot=0;
  for (k=0; k<nset; k++) {
    index[k]=ntot;
    nmx=(nmax-ntot)/(nset-k);
    ERR_I(n=DlogGet(dset, k, nmx, starttime, &x[ntot], &y[ntot]));
    ntot+=n;
  }
  return(ntot);
  OnError:
    return(-1);
}

int Dlog_Get_(char *name, int *nset, int *nmax, double *starttime, float x[], float y[], int index[], int namlen) {
  DlogSet dset;
  char buf[64];
  int ntot;
/* for compaq unix */
  str_trim(buf, name, namlen);
  ERR_I(DlogOpen(&dset, buf, 0));
  ERR_I(ntot=DlogGetMany(&dset, *nset, *nmax, starttime, x, y, index));
  DlogClose(&dset);
  return(ntot);
  OnError:
    ErrShow("error in Dlog_Get");
    return(-1);
}

int Dlog_Get(char **name, int *nset, int *nmax, double *starttime, float x[], float y[], int index[]) {
/* for VMS */
  return(Dlog_Get_(name[1], nset, nmax, starttime, x, y, index, *(short *)name));
}

int DlogUpd(DlogSet *dset) {
  ERR_SI(lseek(dset->fd, 0, SEEK_SET));
  ERR_SI(write(dset->fd, &dset->last, sizeof(int)));
  dset->pos=sizeof(int);
#ifdef __VMS
  close(dset->fd);
  DlogOpen(dset, dset->name, 1);
#endif
  return(0);
  OnError:
    return(-1);
}

int DlogClose(DlogSet *dset) {
  ERR_I(DlogUpd(dset));
  close(dset->fd);
  return(0);
  OnError:
    return(-1);
}
