// Copyright (C) 1995 Jon Kutemeier
// Use is governed by the MiscKit license

/******************************************************************************
 *      $Log$
 ******************************************************************************/
static char *ident = "$Id$";
static int __hack(int x)
{if(x)return x;else return __hack((int)*ident);}
/*****************************************************************************/

#import <libc.h>

#import "SimpleDate.h"

static int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static char *months[] = {"", "January", "February", "March", "April", \
			 "May", "June", "July", "August", "September", \
			 "October", "November", "December"};

@interface SimpleDate (Private)

- _setToday;
- (BOOL)_isValidYear:(int)year month:(int)month day:(int)day;
- (BOOL)_isLeapYear:(int)year;

@end

@implementation SimpleDate

- init
{
    [super init];

    [self _setToday];

    return self;
}

- (int)day
{
    return day;
}

- (int)month
{
    return month;
}

- (int)year
{
    return year;
}

- setYear:(int)aYear month:(int)aMonth day:(int)aDay
{
    if (![self _isValidYear:aYear month:aMonth day:aDay])
      return nil;

    year = aYear;
    month = aMonth;
    day = aDay;

    return self;
}

- (int)numberOfDaysInMonth
{
    if ([self _isLeapYear:year] && month == 2)
      return 29;

    return days[month];
}

- (int)startDayOfMonth
{
    struct tm date;
    time_t time;

    date.tm_sec = 0;
    date.tm_min = 0;
    date.tm_hour = 0;
    date.tm_mday = 1;
    date.tm_mon = month - 1;
    date.tm_year = year - 1900;
    date.tm_wday = 0;
    date.tm_yday = 0;
    date.tm_isdst = 0;
    date.tm_gmtoff = 0;
    date.tm_zone = "";

    time = mktime(&date);

    date = *localtime(&time);

    time -= date.tm_gmtoff;

    date = *localtime(&time);

    return date.tm_wday;
}
    
- incrementMonth
{
    if (month == 12)
      month = 1;
    else
      month++;

    if (day > days[month])
      day = days[month];

    return self;
}

- decrementMonth
{
    if (month == 1)
      month = 12;
    else
      month--;

    if (day > days[month])
      day = days[month];

    return self;
}

- incrementYear
{
    if (year+1 == 2038)
      return nil;

    if ([self _isLeapYear:year] && ![self _isLeapYear:year+1] &&
	month == 2 && day == 29)
      day = 28;

    year++;

    return self;
}

- decrementYear
{
    if (year == 1970)
      return nil;

    if ([self _isLeapYear:year] && ![self _isLeapYear:year-1] &&
	month == 2 && day == 29)
      day = 28;

    year--;

    return self;
}

- (const char *)monthStringValue
{
    return months[month];
}

- (const char *)dateStringValue
{
    sprintf(dateString, "%s %d, %d", months[month], day, year);

    return dateString;
}

- read:(NXTypedStream *)aStream
{
    [super read:aStream];

    NXReadTypes(aStream, "iii", &day, &month, &year);

    return self;
}


- write:(NXTypedStream *)aStream
{
    [super write:aStream];

    NXWriteTypes(aStream, "iii", &day, &month, &year);

    return self;
}

@end

@implementation SimpleDate (Private)

- _setToday
{
    struct tm	*tmstruct;
    time_t	now;

    now = time(0);

    /* Get the tm structure for the time right now */

    tmstruct = localtime(&now);

    /* Grab the year, month and day info from the tm struct */

    year = tmstruct->tm_year + 1900;
    month = tmstruct->tm_mon + 1;
    day = tmstruct->tm_mday;

    return self;
}

- (BOOL)_isValidYear:(int)aYear month:(int)aMonth day:(int)aDay
{
    /* Is it a valid year? We do not accept any date before */
    /* January 1, 1970 */

    if (aYear < 1970)
      return NO;

    /* Is it a valid month? */

    if (aMonth < 1  ||  aMonth > 12)
      return NO;

    /* Is it a valid day? */
    if (aDay < 1)
      return NO;

    /* Is it Feburary on a leap year? */

    if (aDay > (((month == 2) && [self _isLeapYear:aYear])? 29:days[aMonth]))
      return NO;

    /* It is valid! */

    return YES;
}

- (BOOL)_isLeapYear:(int)aYear
{
    if (((aYear % 4) == 0 && ((aYear % 100) != 0)) || (aYear % 400) == 0)
      return YES;

    return NO;
}

@end
