/* ***************************************  
 * *                                     * 
 * *   Time string formatting routines   * 
 * *                                     * 
 * *************************************** 
 * File: libs/user/TimeNames.c
 */     

/*
 *  COPYRIGHT (C) 1983, 1984, 1988, 1993, 1995, 1996, 1998, 2000, 2002,
 *                            2003, 2004  BY
 *          SIGMET INCORPORATED, WESTFORD MASSACHUSETTS, U.S.A.  
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED. 
 */    
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>

#define INCLUDE_SYSTIME_PROTOTYPES 1
#include "sigtypes.h"
#include "user_lib.h"

/* ------------------------------
 * Return a pointer to a 3-char month name
 */
const char *sMonth3( SINT4 iMonth_a )
{
  static const char sMons[12][4] = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", 
				     "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" } ;
  static const char sError[4] = { "xxx" } ;
  
  if( (iMonth_a < 1) || (iMonth_a > 12) ) return( sError ) ;
  else                                    return( sMons[ iMonth_a - 1 ] ) ;
}

/* ------------------------------
 * Convert year,month,day to formatted 11-character string e.g. " 1 JUL 1984"
 */ 
char* sddmonyyyy_r
( SINT4 iyear_a,
  SINT4 imonth_a,
  SINT4 iday_a,
  char sBuffer_a[12] )       /* Always returns 11 chars plus null */
{      
  char sTime[20] ; memset( sTime, 0, 20 ) ;
  sprintf( sTime, "%2d %s %04d", iday_a, sMonth3(imonth_a), iyear_a ) ;
  sTime[11]=0 ; memcpy( sBuffer_a, sTime, 12 ) ;
  return sBuffer_a;
}

/* ------------------------------
 * Convert month 3-letter text to month number, origin 1, zero for no
 * match.
 * History:
 * 13 May 1993  jmh  Moved to user library, replaced identically named routine
 *  5 May 1993  jmh  repaired bug in JUN/JUL and MAY/MAR distinctions
 */
SINT4 imonth_from_txt
( volatile const char* stype_a )
{
  char  stype[4]; SINT4 imon;

  /* copy to local array, and convert to upper case.  Note that we
   * are clipping the string to 3 characters. 
   */
  strncpy( stype, (const char*)stype_a, 3 );
  stype[3]=0;
  STR_UPPER( stype, 3 );

  if      (!strcmp( stype, "JAN" ))
    imon = 1;
  else if (!strcmp( stype, "FEB" ))
    imon = 2;
  else if (!strcmp( stype, "MAR" ))
    imon = 3;
  else if (!strcmp( stype, "APR" ))
    imon = 4;
  else if (!strcmp( stype, "MAY" ))
    imon = 5;
  else if (!strcmp( stype, "JUN" ))
    imon = 6;
  else if (!strcmp( stype, "JUL" ))
    imon = 7;
  else if (!strcmp( stype, "AUG" ))
    imon = 8;
  else if (!strcmp( stype, "SEP" ))
    imon = 9;
  else if (!strcmp( stype, "OCT" ))
    imon = 10;
  else if (!strcmp( stype, "NOV" ))
    imon = 11;
  else if (!strcmp( stype, "DEC" ))
    imon = 12;
  else
    imon = 0;
  
  return imon;
}

/* ------------------------------
 * Return a hh:mm:ss.mmm dd mon yyyy string, given the ymds record.
 * Requires a TIMENAME_SIZE char buffer.
 */
char* shhmmssmmm_ddmonyyyy
( volatile const struct ymds_time *ymds_a,
  char sBuffer_a[TIMENAME_SIZE] )
{

  shhmmssmmm_r( ymds_a->isec, MS_FROM_MILLS(ymds_a->imills), sBuffer_a );
  sBuffer_a[12]=' ';
  sddmonyyyy_r( ymds_a->iyear, ymds_a->imon, ymds_a->iday, &sBuffer_a[13] );

  return sBuffer_a;
}

/* ------------------------------
 * Return a hh:mm:ss.mmm dd mon yyyy string, given the ymds record.
 * Requires a TIMENAME_SIZE char buffer.
 */
char* shhmmssmmm_ddmonyyyy_serv
( volatile const struct serv_ymds_time *ymds_a,
  char sBuffer_a[TIMENAME_SIZE] )
{

  shhmmssmmm_r( ymds_a->isec, NS_FROM_NANOS(ymds_a->inanos)/1000, sBuffer_a );
  sBuffer_a[12]=' ';
  sddmonyyyy_r( ymds_a->iyear, ymds_a->imon, ymds_a->iday, &sBuffer_a[13] );

  return sBuffer_a;
}

/* ------------------------------
 * Return a hh:mm:ss dd mon yyyy string, given the ymds record.
 * Requires a TIMENAME_SIZE char buffer.
 */
char* shhmmssddmonyyyy_r
( volatile const struct ymds_time *ymds_a,
  char sBuffer_a[TIMENAME_SIZE] )
{

  shhmmss_r( ymds_a->isec, sBuffer_a );
  sBuffer_a[8]=' ';
  sddmonyyyy_r( ymds_a->iyear, ymds_a->imon, ymds_a->iday, &sBuffer_a[9] );

  return sBuffer_a;
}

/* ------------------------------
 * Return a hh:mm:ss dd mon yyyy string, given the ymds record 
 */
char* shhmmssddmonyyyy_serv_r
( volatile const struct serv_ymds_time *ymds_a,
  char sBuffer_a[TIMENAME_SIZE] )
{

  shhmmss_r( ymds_a->isec, sBuffer_a );
  sBuffer_a[8] = ' ';
  sddmonyyyy_r( ymds_a->iyear, ymds_a->imon, ymds_a->iday, &sBuffer_a[9] );

  return sBuffer_a;
}

/* ------------------------------
 * Return a hh:mm dd mon yyyy string, given the ymds record.
 * Result is 17-char string with null termination.
 */
char* shhmmddmonyyyy_r
( volatile const struct ymds_time *ymds_a,
  char sBuffer_a[18] )
{
  shhmm_r( ymds_a->isec, sBuffer_a );
  sBuffer_a[5] = ' ';
  sddmonyyyy_r( ymds_a->iyear, ymds_a->imon, ymds_a->iday, &sBuffer_a[6] );

  return sBuffer_a;
}

/* ------------------------------
 * Return a hh:mm dd mon yyyy string, given the ymds record 
 */
char* shhmmddmonyyyy_serv_r
( volatile const struct serv_ymds_time *ymds_a,
  char sBuffer_a[18] )
{

  shhmm_r( ymds_a->isec, sBuffer_a );
  sBuffer_a[5] = ' ';
  sddmonyyyy_r( ymds_a->iyear, ymds_a->imon, ymds_a->iday, &sBuffer_a[6] );

  return sBuffer_a;
}

/* ------------------------------
 * Return a hh:mm:ss.mmm string, given the number of seconds.
 * Maximum displayable value is "99:59:59.999". 
 */
char* shhmmssmmm_r
( const SINT4 isec_a,
  UINT2 iMs_a,          /* Milliseconds */
  char sBuffer_a[13] )  /* Result is 12 chars + null */
{
  shhmmss_r( isec_a, sBuffer_a );
  if (iMs_a<=999)
    sprintf( sBuffer_a+8, ".%03d", iMs_a );
  else
    strcpy( sBuffer_a+8, ".***" );

  return sBuffer_a;
}

/* ------------------------------
 * Return a hh:mm:ss string, given the number of seconds.
 * Maximum displayable value is "99:59:59". 
 */
char* shhmmss_r
( const SINT4 isec_a,
  char sBuffer_a[9] )  /* Result is 8 chars + null */
{
  char stime[16];

  memset( stime, 0, 16 );

  if ( (isec_a >= 0)&&(isec_a < 100*3600) )
    {
      sprintf( &stime[0], "%02d:", isec_a / 3600       );
      sprintf( &stime[3], "%02d:",(isec_a % 3600) / 60 );
      sprintf( &stime[6], "%02d", isec_a % 60         );
      stime[8]=0;
    }
  else
    {
      strcpy( stime, "**:**:**" );
    }
  memcpy( sBuffer_a, stime, 9 );
  return sBuffer_a;
}

/* ------------------------------
 * Return a hh:mm string, given the number of seconds.
 * Maximum displayable value is "99:59".
 */
char* shhmm_r
( const SINT4 isec_a,
  char sBuffer_a[6] )  /* Result is 5 chars + null */
{
  char stime[16];

  memset( stime, 0, 16 );

  if ( (isec_a >= 0)&&(isec_a < 100*3600) )
    {
      sprintf( &stime[0], "%02d:", isec_a / 3600       );  
      sprintf( &stime[3], "%02d",(isec_a % 3600) / 60 );  
      stime[5]=0;
    }
  else
    {
      strcpy( stime, "**:**" );
    }

  memcpy( sBuffer_a, stime, 6 );
  return sBuffer_a;
}

/* ------------------------------
 * Convert character string to seconds.
 */
SINT4 ihhmmss
( volatile const char* stime_a )
{
  char  *stoks[4], shhmmss[16]; SINT4 inums[4], ii ;

  strncpy( shhmmss, (const char*)stime_a, 15 ); shhmmss[15] = 0;
  inums[0] = inums[1] = inums[2] = 0;  

  ii= 0; stoks[ii] = strtok( shhmmss, " :,." );
  while( stoks[ii]!= NULL )
    {       
      inums[ii] = atoi( stoks[ii] );
      ii++; stoks[ii] = strtok( NULL, " :,." );
    };

  return (inums[0]*3600+inums[1]*60+inums[2]);
}

/* ------------------------------
 * Convert time in a string to ymds time structure.  If an error is
 * found, it will return all zeros in the structure.  It tolerates
 * extra spaces and other deliminators.
 */
void shmsdmy_to_ymds
( volatile const char* stime_a,
  volatile struct ymds_time *ymds_a )
{
  char  *stoks[10], shmsdmy[31]; SINT4 ii;

  /* Tolerate up to 10 extra characters than normally expected.
   */
  strncpy( shmsdmy, (const char*)stime_a, 30 ); shmsdmy[30] = 0;

  ii= 0; 
  stoks[ii] = strtok( shmsdmy, " :,." );
  while ( (stoks[ii]!= NULL) && (ii<10) )
    { 
      ii++; stoks[ii] = strtok( NULL, " :,." ); 
    }
  /* It is an error if there are not at least 6 tokens
   */
  if (ii>=6)
    {
      ymds_a->isec  = atoi(stoks[0])*3600 + atoi(stoks[1])*60 + atoi(stoks[2]);
      ymds_a->iday  = atoi(stoks[3]);
      ymds_a->imon  = imonth_from_txt( stoks[4] );
      ymds_a->iyear = atoi(stoks[5]);
    }
  else
    {
      ymds_a->isec  = 0;
      ymds_a->iday  = 0;
      ymds_a->imon  = 0;
      ymds_a->iyear = 0;
    }
}

/* ------------------------------
 * Builds a Time Zone name from some available information.  This routine
 * is designed to generate the currrent TZname based on data fields which
 * might not have been initialized in the input data, because old data 
 * did not fill them in.  All non-printable characters are removed from 
 * the input string.  If at least one character is not there, the
 * fall back to displaying a numerical time based on the minutes west.
 * If this is out of range, then use "???".
 */
void TZNameBuild
( char sTZNameOut_a[TZNAME_SIZE],
  const char *sTZNameIn_a,  /* Local TZ name */
  SINT4 iMinutesWest_a,     /* Recorded TZ offset */
  UINT2 iFlags_a )          /* Flags indicating the type of TZ recording */
{
  int iLength, iPos;

  if (UTC_FROM_MILLS( iFlags_a ))
    {
      strcpy( sTZNameOut_a, "UTC" );
      return;
    }
  /* Copy string */
  strncpy( sTZNameOut_a, sTZNameIn_a, TZNAME_SIZE-1 );
  sTZNameOut_a[TZNAME_SIZE-1]=0;

  /* Filter out bad characters */
  iLength = strlen( sTZNameOut_a );
  for (iPos=0; iPos<iLength; iPos++)
    {
      if (!isgraph(sTZNameOut_a[iPos])) sTZNameOut_a[iPos] = 0;
    }

  iLength = strlen( sTZNameOut_a );
  if (iLength<1)
    {
      /* Use numerical format because the string was invalid */
      SINT4 iHours = -(iMinutesWest_a/60);
      if ((iHours>-18) && (iHours<18))
	{
	  sprintf( sTZNameOut_a, "%+03d", iHours );
	}
      else
	{
	  strcpy( sTZNameOut_a, "???" );
	}
    }
}
