/* Implementation of Objective-C Time object
   Copyright (C) 1993 Free Software Foundation, Inc.

   Written by:  R. Andrew McCallum <mccallum@cs.rochester.edu>
   Dept. of Computer Science, U. of Rochester, Rochester, NY  14627

   This file is part of the GNU Objective-C Collection library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
   
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ 

#include <coll/Time.h>

/* tmp for passing to gettimeofday() */
static struct timeval _Time_tv;
static struct timezone _Time_tz;

/* There are several places where I need to deal with tz more intelligently */

@implementation Time

+ initialize
{
  if (self == [Time class])
    {
      [self setVersion:-1];	/* alpha release */
      // init day and month names here;
    }
  return self;
}

+ (long) secondClockValue
{
  gettimeofday(&_Time_tv, &_Time_tz);
  return _Time_tv.tv_sec;
}

+ getClockValueSeconds: (long *)sec microseconds: (long *)usec
{
  gettimeofday(&_Time_tv, &_Time_tz);
  *sec = _Time_tv.tv_sec;
  *usec = _Time_tv.tv_usec;
  return self;
}
  
+ (long) millisecondsToRun: (void(*)())aFunc
{
  struct rusage start_ru, end_ru;
  
  getrusage(RUSAGE_SELF, &start_ru);
  aFunc();
  getrusage(RUSAGE_SELF, &end_ru);
  return ((end_ru.ru_utime.tv_sec - start_ru.ru_utime.tv_sec +
	   end_ru.ru_stime.tv_sec - start_ru.ru_stime.tv_sec) * 1000 + 
	  (end_ru.ru_utime.tv_usec - start_ru.ru_utime.tv_usec +
	   end_ru.ru_stime.tv_usec - start_ru.ru_stime.tv_usec) / 1000);
  /* should add a warning on overflow. */
}

+ getSeconds: (long *)sec microseconds: (long *)usec toRun: (void(*)())aFunc
{
  struct rusage start_ru, end_ru;

  [self notImplemented:_cmd];
  getrusage(RUSAGE_SELF, &start_ru);
  aFunc();
  getrusage(RUSAGE_SELF, &end_ru);

  return self;
}

+ (unsigned) indexOfDayName: (const char *)dayName
{
  [self notImplemented:_cmd];
  return 0;
}

+ (const char *) nameOfDayIndex: (unsigned)dayIndex
{
  [self notImplemented:_cmd];
  return "";
}

+ (unsigned) indexOfMonthName: (const char *)monthName
{
  [self notImplemented:_cmd];
  return 0;
}

+ (const char *) nameOfMonthIndex: (unsigned)monthIndex
{
  [self notImplemented:_cmd];
  return "";
}

+ (unsigned) daysInMonthIndex: (unsigned)monthIndex forYear: (unsigned)year
{
  [self notImplemented:_cmd];
  return 0;
}

+ (unsigned) daysInYear: (unsigned)year
{
  [self notImplemented:_cmd];
  return 0;
}

+ (BOOL) leapYear: (unsigned)year
{
  [self notImplemented:_cmd];
  return NO;
}


- initNow
{
  [super init];
  gettimeofday(&tv, &tz);
  return self;
}

- initDayIndex: (unsigned)dayIndex 
    monthIndex: (unsigned)monthIndex 
    year: (unsigned)year
{
  [self notImplemented:_cmd];
  [super init];
  return self;
}

- initSeconds: (long)numSeconds microseconds: (long)numMicroseconds
{
  [super init];
  gettimeofday(&tv, &tz);	/* to fill tz */
  tv.tv_sec = numSeconds;
  tv.tv_usec = numMicroseconds;
  return self;
}

- initSeconds: (long)numSeconds
{
  [super init];
  gettimeofday(&tv, &tz);	/* to fill tz */
  tv.tv_sec = numSeconds;
  tv.tv_usec = 0;
  return self;
}

- setSeconds: (long)numSeconds microseconds: (long)numMicroseconds
{
  tv.tv_sec = numSeconds;
  tv.tv_usec = numMicroseconds;
  return self;
}

- setSeconds: (long)numSeconds
{
  tv.tv_sec = numSeconds;
  return self;
}

- (long) days
{
  return tv.tv_sec / (60 * 60 * 24);
}

- (long) hours
{
  return tv.tv_sec / (60 * 60);
}

- (long) minutes
{
  return tv.tv_sec / 60;
}

- (long) seconds
{
  return tv.tv_sec;
}

- (long) microseconds;
{
  return tv.tv_usec;
}


/* I should do something smart with tz */
  
- addTime: (Time *)aTimeObj
{
  tv.tv_sec += [aTimeObj seconds];
  tv.tv_usec += [aTimeObj microseconds];
  return self;
}

- addDays: (unsigned)num
{
  tv.tv_sec += num * 60 * 60 * 24;
  return self;
}

- addHours: (unsigned)num
{
  tv.tv_sec += num * 60 * 60;
  return self;
}

- addMinutes: (unsigned)num
{
  tv.tv_sec += num * 60;
  return self;
}

- addSeconds: (unsigned)num
{
  tv.tv_sec += num;
  return self;
}


- subtractTime: (Time *)aTimeObj
{
  tv.tv_sec -= [aTimeObj seconds];
  tv.tv_usec -= [aTimeObj microseconds];
  return self;
}

- subtractDays: (unsigned)num
{
  tv.tv_sec -= num * 60 * 60 * 24;
  return self;
}

- subtractHours: (unsigned)num
{
  tv.tv_sec -= num * 60 * 60;
  return self;
}

- subtractMinutes: (unsigned)num
{
  tv.tv_sec -= num * 60;
  return self;
}

- subtractSeconds: (unsigned)num
{
  tv.tv_sec -= num;
  return self;
}


- printForDebugger
{
  if ([self days])
    printf("%d days, %d:%d:%d.%3d\n", 
	   [self days], [self hours], [self minutes], 
	   [self seconds], [self microseconds]);
  else
    printf("%d:%d:%d.%3d\n", [self hours], [self minutes], 
	   [self seconds], [self microseconds]);
  return self;
}

- (BOOL) isEqual: anObject
{
  if ([anObject isKindOf:[Time class]]
      && [anObject seconds] == tv.tv_sec
      && [anObject microseconds] == tv.tv_usec)
    return YES;
  else
    return NO;
}

- (int) compare: anObject
{
  int diff;
  
  if (![anObject isKindOf:[Time class]])
    return 17;	/* what non-zero should be returned in cases like this? */
  diff = tv.tv_sec - [anObject seconds];
  if (diff)
    return diff;
  diff = tv.tv_usec - [anObject microseconds];
  return diff;
}

- write: (TypedStream*)aStream
{
  // archive inst vars;
  [self notImplemented:_cmd];
  [super write:aStream];
  return self;
}

- read: (TypedStream*)aStream
{
  // archive inst vars;
  [self notImplemented:_cmd];
  [super read:aStream];
  return self;
}

@end

