/****************************************************************************/
/* Copyright (c) 2000 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : Time.cc                                                       */
/* Author   :                                                               */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 02/07/2000                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
#include <stdio.h>
#include <string.h>
#include "TimeP.h"

static char Time::_monthDays[2][13] = 
{
  0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
  0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
};


static const int Time::SecondsPerDay = 86400;
static const int Time::SecondsPerHour = 3600;
static const int Time::SecondsPerMinute = 60;

Boolean Time::_epochAssigned = False;
struct timespec Time::_epoch = {0, 0};

double Time::seconds(timespec *timeSpec)
{
  return (timeSpec->tv_sec + timeSpec->tv_nsec / 1.e9);
}


unsigned long Time::milliseconds()
{
  struct timespec timeNow;
 
  clock_gettime(CLOCK_REALTIME, &timeNow);
  
  if (!_epochAssigned) {
    // Initialize epoch the first time through 
    _epoch.tv_sec  = timeNow.tv_sec;
    _epoch.tv_nsec = timeNow.tv_nsec;
    _epochAssigned = True;
  } 

  long delta = 
    (timeNow.tv_sec - _epoch.tv_sec) * 1000 + 
      (timeNow.tv_nsec - _epoch.tv_nsec) / 1.e6;

  return (unsigned long )delta;
}

void Time::gettime(TimeIF::TimeSpec *timeSpec)
{
  struct timespec timeNow;
 
  clock_gettime(CLOCK_REALTIME, &timeNow);
  timeSpec->seconds     = timeNow.tv_sec;
  timeSpec->nanoSeconds = timeNow.tv_nsec;
} 



void Time::secsToHourMinSec(double secs, char *timestring)
{
  int days;
  int hrs;
  int min;
  int isecs;
  
  isecs = secs;
  
  if ((days = isecs / SecondsPerDay) > 0)
  {
    isecs = isecs % SecondsPerDay;
  }
  if ((hrs = isecs / SecondsPerHour) > 0)
  {
    isecs = isecs % SecondsPerHour;
  }
  if ((min = isecs / SecondsPerMinute) > 0)
  {
    isecs = isecs % SecondsPerMinute;
  }
  secs = isecs + (secs - (int )secs);
  
  sprintf(timestring, "%03d:%02d:%02d:%02.1f", days, hrs, min, secs);

  return;
}


int Time::hourMinSecToSecs(char *timestring, double *secs)
{
  char *token;
  char buf[100];
  char *bufptr;
  int nflds = 0;
  double val[4];

  strncpy(buf, timestring, sizeof(buf)-1);
  bufptr = buf;
  while ((token = strtok(bufptr, ":")) != NULL)
  {
    bufptr = NULL;
    if (sscanf(token, "%lf", &val[nflds]) <= 0)
    {
      return -1;
    }
    nflds++; 
    if (nflds > 4)
      return -1;
  }

  *secs = 0.;
  
  switch (nflds)
  {
    case 4:
    /* Specified days, hours, mins, secs */
    *secs += val[0] * SecondsPerDay;
    *secs += val[1] * SecondsPerHour;
    *secs += val[2] * SecondsPerMinute;
    *secs += val[3];
    return 0;
    break;
    
    case 3:
    /* Specified hours, mins, secs */
    *secs += val[0] * SecondsPerHour;
    *secs += val[1] * SecondsPerMinute;
    *secs += val[2];
    return 0;
    break;
    

    default:
    return -1;
    
  }
}


Boolean Time::leapYear(int year)
{
  return (year % 4 == 0) && (year % 100 != 0) || year % 400 == 0;
}


int Time::daysInYear(int year)
{
  if (leapYear(year))
    return 366;
  else
    return 365;
}


int Time::dayOfYear(int year, int month, int day)
{
  int i, leap;
  if (year < 0 || month < 1 || month > 12 || day < 1 || day > 31)
    return -1;
  
  leap = (year % 4 == 0) && (year % 100 != 0) || year % 400 == 0;
  for (i = 1; i < month; i++)
    day += _monthDays[leap][i];
  
  return day;
}


int Time::dayOfYearToMonthDay(int doy, int year, int *month, int *day)
{
  Boolean leap;
  int sum;

  leap = leapYear(year);
  if (doy < 1 || (!leap && doy > 365) || (leap && doy > 366))
    return -1;
  
  *month = 0;
  for (sum = 0, *month = 0; sum + _monthDays[leap][*month] < doy; 
       sum += _monthDays[leap][*month], (*month)++)
  {
    ;
  }
  *day = doy - sum;  
  return 0;
}
