/** Utility methods from X-Midas. Be sure to update the header file cutils.h
    when modifying this file.

    @author  X-Midas
    @version $Id: cutils.c,v 1.8 2010/11/01 21:37:29 ntn Exp $
 */
#include <time.h>
#include <sys/time.h>
#include <unistd.h>    /* Contains usleep(..) */
#include "cdefs.h"
#include "headers.h"
#include "midas.h"
#include "fcinterface.h"
#include "cutils.h"
#include <stdlib.h>    /* for malloc(), free() */
#include <stdio.h>     /* for printf(), sprintf() */
#include <ctype.h>     /* for toupper() */
#include <string.h>    /* for strlen(), strncpy() */
#include <strings.h>   /* for bcopy() */

/*
  Utilities
*/
void m$pause_ (float *delay) {
  int mdelay = (int)( 1000000*(*delay) );
  usleep(mdelay);
}

void m$move_ (int_4 *buf1, int_4 *buf2, int_4 *elements) {
  if (*elements <= 0) return;
  if (buf1 == buf2) return;
  bcopy((void *) buf1, (void *) buf2, (size_t)*elements);
}

/* M$LENGTH is a HIGH PROFILE routine */
int_4 m$length_(const char *string, fstring_len_t ls) {
  int i;
  for (i = ls - 1; i >= 0  &&  string[i] <= ' '; i--);
  return i + 1;  /* meant for FORTRAN */
}

/* M$LENGTHD is another HIGH PROFILE routine */
int_4 m$lengthd_ (const char *string, fstring_len_t ls) {
  int   quote = 0;
  int   paren = 0;
  bool  inswitch;

  register char c;
  register int  i;

  inswitch = (string[0] == '=');
  for (i = 0; i < ls; i++) {
    c = string[i];

/* If this is not a delimiter character, make no further tests */
    if (c >= '0'  &&  c <= 'Z') continue;

/* Within double quotes, the only important character is a double quote */
    if (quote) {
      if (c == '"') quote = !quote;
      continue;
    }

/* Outside of double quotes. */
    switch (c) {
      case ',': case ' ':
        if (paren == 0) return i;
        break;
      case '"':
        quote = !quote;
        break;
      case '(': case '{': case '[':
        paren++;
        break;
      case ')': case '}': case ']':
        if (paren > 0) paren--;
        break;
      case '/':
        if (inswitch && (!quote && paren == 0)) return i;
        break;
      default:
        if (c <= ' '  &&  paren == 0) return i;
    } /* switch */

  } /* for */

  if (paren == 0) return ls;

  for (i = ls - 1; i >= 0  &&  string[i] <= ' '; i--);  /* like M$LENGTH */
  return i + 1;                                         /* meant for FORTRAN */
}

real_4 secnds(real_4 *ref) {
  time_t nowclock;
  struct tm res;
  struct timeval tp;
  real_4 sincemidnight,sinceref;

  gettimeofday(&tp,NULL);
  nowclock=tp.tv_sec;
  localtime_r(&nowclock,&res);

  sincemidnight=res.tm_sec+res.tm_min*60+res.tm_hour*3600+(tp.tv_usec/1000000.0);

  if ((*ref) > sincemidnight)
    sinceref=((86400.0+sincemidnight)-(*ref));
  else
    sinceref=(sincemidnight-(*ref));

  sinceref = (sinceref > 0.0)     ? (sinceref) : (0.0);
  sinceref = (sinceref < 86400.0) ? (sinceref) : (86400.0);

  return sinceref;
}

/** A variant of strncpy that takes in offsets into the strings. */
static char *strncpy2(char *dest, int i, const char *src, int j, size_t n) {
  for (; n != 0; i++,j++,n--) { /* n is unsigned */
    dest[i] = src[j];
    if (dest[i] == 0) break;
  }
  return dest;
}

int_4 m$clean_(const char *string, char *cstring, fstring_len_t ls, fstring_len_t lcs) {
  char  ci;
  char  clast = ' ';
  int   paren = 0;
  int   i, j, k, length;
  int   ii,jj;

/* Replace space/tab delimiters with commas and convert to UPCASE */
  for (i = j = 0; i < ls  &&  j < lcs; i++) {
    ci = string[i];
    if (ci <= ' '  &&  paren == 0) {
      if (clast != ' '  &&  clast != ',') {
        cstring[j++] = ',';
        clast = ' ';
      }
    }
    else if (ci == ',') {
      if (clast != ' ') cstring[j++] = ',';
      clast = ',';
    }
    else if (ci == '"') {
      for (k = i + 1; k < ls && string[k] != '"'; k++);
      if (k < ls) {
        length = k - i + 1;
        strncpy2(cstring, j, string, i, length);
        j += length;
        i += length - 1;
      }
      else {
        length = m$length_(string + i, ls - i);
        strncpy2(cstring, j, string, i, length);
        j = j + length;
        cstring[j++] = '"';
        cstring[j++] = ',';
        i = ls;
      }
      clast = '"';
    }
    else { /* all other characters */
      if (ci >= 'a'  &&  ci <= 'z') {
        ci = toupper(ci);
      } else if (ci >= ')'  &&  ci <= ']') {
        switch (ci) {
          case '(': case '[': case '{':
            paren++;
            break;
          case ')': case ']': case '}':
            if (paren > 0) paren--;
            break;
          default:
            ci = toupper(ci);
            break;
        }    /* switch */
      }
      cstring[j++] = clast = ci;
    }
  }          /* for */

/*
At the end of the string.  We may be here due to an unbalanced bracketing
character.  If this is the case, we need to trim back all white space.
*/
  if (paren != 0) {
    printf("Unbalanced bracketing characters in M$CLEAN");
    j = m$length_(cstring, j);
  }

/*
Now we still have to be careful to check the length of the string to
be sure that we have room for the trailing comma, CHAR(0), and spacing.
*/
  if (j < lcs  &&  cstring[j - 1] != ',') cstring[j++] = ',';

/* Tack on a trailing NULL to mark the string as CLEANED (if there's room) */
  if (j < lcs)     cstring[j] = 0;
  if (j + 1 < lcs) cstring[j + 1] = ' ';

  return j;
}

int m_cformat(char *value, char type, char *target, char *format, fstring_len_t tlen, fstring_len_t flen) {
  char *temp;
  int  i;

  if (flen<=0) return -1;
  temp = (char *) malloc (flen+1);
  strncpy (temp, format, flen);
  temp[flen] = 0;
  switch (type) {
    case 'B': sprintf (target, temp, *((unsigned char *) value)); break;
    case 'I': sprintf (target, temp, *((short         *) value)); break;
    case 'L': sprintf (target, temp, *((long          *) value)); break;
    case 'F': sprintf (target, temp, *((float         *) value)); break;
    case 'D': sprintf (target, temp, *((double        *) value)); break;
    case 'A': sprintf (target, temp, *((char          *) value)); break;
    default: target[0] = 0;
  }
  free (temp);

  i = strlen(target);
  strc2f (target, i, target, tlen, C2F_FILL);
  return (i);
}

/* C string formatting access from FORTRAN */
int m$cformat_ (char *val, char *type, char *str, char *fmt, fstring_len_t ltype, fstring_len_t lstr, fstring_len_t lfmt)
{
  return ( m_cformat(val,type[0],str,fmt,lstr,lfmt) );
}

