/************************************************************************/
/* Copyright 2003-2012 MBARI						*/
/************************************************************************/
/* Summary  : Definitions for Utility library for BEDS			*/
/* Filename : utils.c							*/
/* Author   : Robert Herlien (rah)					*/
/* Project  : Benthic Event Detection System (BEDS)			*/
/* Revision: 1.0							*/
/* Created  : 09/28/2012 from OASIS utils.h				*/
/*									    */
/* MBARI provides this documentation and code "as is", with no warranty,    */
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium  */
/* Research Institute to assist in its use, correction, modification, or    */
/* enhancement. This information should not be published or distributed to  */
/* third parties without specific written permission from MBARI.            */
/*									    */
/************************************************************************/
/* Modification History:						*/
/* 28sep2012 rah - created from OASIS utils.c				*/
/* $Log$
 */
/************************************************************************/

#include <mbariTypes.h>			/* MBARI type definitions	    */
#include <mbariConst.h>			/* MBARI constants		    */
#include <beds.h>			/* BEDS controller definitions	    */
#include <utils.h>			/* BEDS Utility routines	    */

#include <stdio.h>			/* Standard I/O library		    */
#include <stdarg.h>			/* vararg stuff			    */
#include <stdlib.h>			/* Standard C library		    */
#include <string.h>			/* String library functions	    */
#include <ctype.h>			/* Standard ctype.h defs	    */
#include <time.h>


/****************************************/
/*	Global Data			*/
/****************************************/

Global char	*timespecErr = NULL;


/****************************************/
/*	Module Local Data		*/
/****************************************/

MLocal char	*okStr = "OK";
MLocal char	*dateErr = "Error in date string";
MLocal char	*timeErr = "Error in time string";
MLocal char	*unkErr = "Unknown time/date string";


/************************************************************************/
/* Function    : delimit						*/
/* Purpose     : Determine if character is a delimiter			*/
/* Inputs      : Character						*/
/* Outputs     : TRUE if character is ',', NULL, or space		*/
/************************************************************************/
MBool delimit( Reg char c )
{
    return( isspace(c) || (c == '\0') || (c == ',') );

} /* delimit() */


/************************************************************************/
/* Function    : removeNewline						*/
/* Purpose     : Remove line terminator of \n or \r			*/
/* Inputs      : String ptr						*/
/* Outputs     : None							*/
/************************************************************************/
Void removeNewline(char *s)
{
    char *p;

    if ((p = strchr(s, '\n')) != NULL)
	*p = '\0';

    if ((p = strchr(s, '\r')) != NULL)
	*p = '\0';

} /* removeNewline() */


#ifndef STRNFUNCS

/************************************************************************/
/* Function    : strcasecmp						*/
/* Purpose     : Compare strings, case insensitive			*/
/* Inputs      : 2 string ptrs						*/
/* Outputs     : An integer less than, equal to, or greater than zero,	*/
/*		 depending on if s1 < s2, s1 == s2, or s1 > s2		*/
/* Comments    : Tried to make this identical to std C strcasecmp(),	*/
/*		 which is missing from the Metrowerks C RTL		*/
/************************************************************************/
int strcasecmp(const char *s1, const char *s2)
{
    const char	*p1 = s1;
    const char	*p2 = s2;
    char	c1, c2;

    while ((c1 = toupper(*p1)) == (c2 = toupper(*p2)))
    {
	if (c1==0)
	    return((c2==0) ? 0 : -(unsigned)c2);
	p1++;
	p2++;
    }

    return((unsigned)c1 - (unsigned)c2);

} /* strcasecmp() */


/************************************************************************/
/* Function    : strncasecmp						*/
/* Purpose     : Compare strings, case insensitive			*/
/* Inputs      : 2 string ptrs, length to match				*/
/* Outputs     : An integer less than, equal to, or greater than zero,	*/
/*		 depending on if s1 < s2, s1 == s2, or s1 > s2		*/
/* Comments    : Tried to make this identical to std C strncasecmp(),	*/
/*		 which is missing from the Metrowerks C RTL		*/
/************************************************************************/
int strncasecmp(const char *s1, const char *s2, int len)
{
    const	char	*p1 = s1;
    const	char	*p2 = s2;
    char	c1, c2;
    int		n = len;

    if (len <= 0)
	return(0);

    while ((n-- > 0) && ((c1 = toupper(*p1)) == (c2 = toupper(*p2))))
    {
	if (!c1)
	    return(0);
	p1++;
	p2++;
    }

    return((unsigned)c1 - (unsigned)c2);

} /* strncasecmp() */

#endif /* !STRNFUNCS */


/************************************************************************/
/* Function    : readWord						*/
/* Purpose     : Get 16 bit word from input stream in big endian format	*/
/* Inputs      : FILE Ptr						*/
/* Outputs     : 16 bit value						*/
/************************************************************************/
Nat16 readWord(FILE *fp)
{
    Nat16	wrd;

    fread(&wrd, sizeof(wrd), 1, fp);
    return(wrd);
}


/************************************************************************/
/* Function    : readLong						*/
/* Purpose     : Get 32 bit long from input stream in big endian format	*/
/* Inputs      : FILE Ptr						*/
/* Outputs     : 32 bit value						*/
/************************************************************************/
Nat32 readLong(FILE *fp)
{
    Nat32	lng;

    fread(&lng, sizeof(lng), 1, fp);
    return(lng);
}


/************************************************************************/
/* Function    : getDuration						*/
/* Purpose     : Parse string for a duration in seconds			*/
/* Inputs      : Duration string					*/
/* Outputs     : Duration in seconds, or ERROR				*/
/* Comment     : Trailing s/m/h/d denotes seconds/minutes/hours/days	*/
/************************************************************************/
Int32 getDuration(char *spec)
{
    char	c;
    Int32	len = -1;
    int		numItems;

    numItems = sscanf(spec, " %ld%c", &len, &c);

    if ((numItems < 1) || (len <= 0))
	return(ERROR);

    if (numItems == 1)
	return(len);

    switch(toupper(c))
    {
      case 'D':
	  if (len <= 0x7fffffff/(24*3600))
	      return(24 * 3600 * len);
	  break;

      case 'H':
	  if (len <= 0x7fffffff/3600)
	      return(3600 * len);
	  break;
	  
      case 'M':
	  if (len <= 0x7fffffff/60)
	      return(60 * len);
	  break;

      case 'S':
          return(len);
    }

    return(ERROR);

} /* getDuration() */


/************************************************************************/
/* Function    : getTimespec						*/
/* Purpose     : Parse command-line spec for time and date		*/
/* Inputs      : Time string, as yyyy/mm/ddThh:mm:ss			*/
/* Outputs     : time_t corresponding to timspec, or 0 if error		*/
/************************************************************************/
time_t getTimespec(char *spec)
{
    char	*p, *curp;
    time_t	when;
    struct tm	spectm, *tmp;
    Int32	len, hour, min, sec, year;
    int		numD, numT;

    timespecErr = okStr;
    memset(&spectm, 0, sizeof(spectm));
    time(&when);
    tmp = gmtime(&when);
    memcpy(&spectm, tmp, sizeof(struct tm));
    spectm.tm_hour = 0;
    spectm.tm_min = 0;
    spectm.tm_sec = 0;
    curp = spec;
    numD = 0;

    if ((p = strchr(spec, '/')) != NULL)
    {
	if ((p = strchr(p+1, '/')) != NULL)
	{				/* Got mm/dd/yyyy		*/
	    if (sscanf(spec, " %ld/%ld/%ld%n",
		       &spectm.tm_mon, &spectm.tm_mday, &spectm.tm_year, &len) < 3)
		return(0);
	    if ((spectm.tm_mon > 12) && (spectm.tm_year <= 31))
	    {				/* Perhaps they entered yyyy/mm/dd ? */
		year = spectm.tm_year;
		spectm.tm_year = spectm.tm_mon;
		spectm.tm_mon = spectm.tm_mday;
		spectm.tm_mday = year;
	    }
	    spectm.tm_year -= 1900;
	    spectm.tm_mon -= 1;
	    curp = spec + len;
	    numD = 3;
	}
	else				/* Got mm/dd			*/
	{
	    if (sscanf(spec, " %ld/%ld%n", &spectm.tm_mon, &spectm.tm_mday, &len) < 2)
		return(0);
	    spectm.tm_mon -= 1;
	    curp = spec + len;
	    numD = 2;
	}

	if ((spectm.tm_year < 70) || (spectm.tm_year > 200) ||
	    (spectm.tm_mon < 0) || (spectm.tm_mon > 11) ||
	    (spectm.tm_mday < 1) || (spectm.tm_mday > 31))
	{
	    timespecErr = dateErr;
	    return(0);
	}

	if (isspace(*curp))
	    deblank(curp);
	else if (*curp)
	    curp++;
    }

    numT = sscanf(curp, " %ld:%ld:%ld", &hour, &min, &sec);

    if ((numD <= 0) && (numT < 2))
    {
	timespecErr = unkErr;
	return(0);
    }

    if (numT >= 2)
    {
	spectm.tm_hour = hour;
	spectm.tm_min = min;
    }
    if (numT >= 3)
	spectm.tm_sec = sec;

    if ((spectm.tm_hour < 0) || (spectm.tm_hour > 23) ||
	(spectm.tm_min < 0) || (spectm.tm_min > 59) ||
	(spectm.tm_sec < 0) || (spectm.tm_sec > 59))
    {
	timespecErr = timeErr;
	return(0);
    }

    return(mktime(&spectm));

} /* getTimespec() */


/************************************************************************/
/* Function    : printTimespec						*/
/* Purpose     : Print explanation of timespec				*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
Void printTimespec(MBool lng)
{
    printf("\n\tTimespec is of form: mm/dd/yyyyThh:mm:ss\n");
    if (lng)
    {
	printf("\t\tEither date portion or time portion may be omitted\n");
	printf("\t\tDate portion may omit year, time portion may omit seconds\n");
    }
}
