/****************************************************************************/
/* 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"
#include "FastTime.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;
static FastTime* Time::_fastTime=NULL;

Time::Time()
{
    Boolean debug=True;
    dprintf("\n****Time::Time ctor[%p]\n",this);
}
virtual Time::~Time()
{
    Boolean debug=True;
    dprintf("\n****Time::Time dtor[%p]\n",this);
    //    if (Time::_fastTime!=NULL) {
    //        dprintf("\n****Time::Time dtor [%p] deleting FastTime [%p]\n",this,Time::_fastTime);
    //        delete Time::_fastTime;
    //    }
}
void Time::cleanup(){
    Boolean debug=True;
    if (Time::_fastTime!=NULL) {
        dprintf("\n****Time::cleanup FastTime instance[%p]]\n",Time::_fastTime);
        delete Time::_fastTime;
        Time::_fastTime=NULL;
    }
}


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

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

double Time::seconds(TimeIF::TimeSpec *timeSpec)
{
  return (timeSpec->seconds + timeSpec->nanoSeconds / 1.e9);
}


unsigned long Time::milliseconds()
{
  struct timespec timeNow;
 
  Time::gettime(&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(struct timespec *timeSpec)
{
    Boolean debug=True;
#ifdef FASTTIME
    static int fastTimeState=-1;
    if (fastTimeState<0) {
        char *fast_time_en=getenv(FAST_TIME_ENV);
        
        if (fast_time_en && (strcmp(fast_time_en,FAST_TIME_EN)==0) && Time::_fastTime==NULL) {
            fastTimeState=1;
            // create a FastTime instance
            Time::_fastTime=new FastTime();
            atexit(Time::cleanup);
            dprintf("\n****Time::gettime using FastTime instance [%p]\n",_fastTime);
        }else{
            fastTimeState=0;
		}
    }
    
    if (Time::_fastTime) {
        _fastTime->getFastTime(timeSpec);
    }else
#endif
    	clock_gettime(CLOCK_REALTIME, timeSpec);

//  printf(":\t\t\t\tTime::gettime() - seconds %ld\n", timeSpec->tv_sec);
} 

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

void Time::getEpoch(timespec *timeSpec)
{
  timeSpec->tv_sec     = _epoch.tv_sec;
  timeSpec->tv_nsec    = _epoch.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;
}
