//
// calendar.m
// Copyright (c) 1989, 1990 by Jiro Nakamura 
// All rights reserved
//
// Calendar routine package.
//
// RCS Information
// Revision Number->	$Revision: 2.7 $
// Last Revised->	$Date: 91/12/20 14:04:59 $
//
static char rcsid[] = "$Header: /shaman330/Users/jiro/Programming/Cassandra/src/RCS/calendar.m,v 2.7 91/12/20 14:04:59 jiro Exp Locker: jiro $";

#import <stdio.h>
#import <c.h>
#import <ctype.h>
#import <appkit/Panel.h>       /* for NXRunAlertPanel */
#import "calendar.h"
#import "misc.h"

const int ayear[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273,
			 304, 334, 365};
const char *months[12]= {"January", "February", "March", "April", "May", 
			"June", "July","August","September",
			"October","November","December"};
const char *shortMonths[12] ={"Jan", "Feb", "Mar", "Apr",  "May",
				"Jun", "Jul", "Aug", "Sep", 
  				"Oct", "Nov", "Dec"};
	
const char *shortWeekDays[7] = {"Sun", "Mon", "Tue", "Wed",
				 "Thu", "Fri", "Sat"};
				 
const char *weekDays[7] = {"Sunday", "Monday", "Tuesday", "Wednesday",
				 "Thursday", "Friday", "Saturday"};


void dumbRcsidReference()
{
    char	dumbChar[5];
    
    // dumb references to some static constants, just
    // to avoid warning messages from the compiler (Garance)
    dumbChar[0] = rcsid[0];		// dumb de dumb

     return;
}

// 
// Returns what day of the week in <year> will be. (0-6, 0=Sunday, 6= Saturday)
//
int jan1(int year)
{

	int day;
	
	day = year + 4 + ((year +3) /4);
	if( year > 1800) 
		{
		day -= ((year - 1701) / 100);
		day += ((year - 1601) / 400);
		}
	if( year > 1752)
		day += 3;
	
	return( day % 7);
}

//
// Returns how many days in the month for that year
//  # Have to watch out, because sometimes we're called with
//  # a value for month that's out-of-range.  Sigh.  Also note
//  # that we're expecting a range of 1-12, not 0-11 like all
//  # other month-related routines would do.  My My.  Need some
//  # contract-based programming here (as in, Eiffel-style).
//  #                Garance/Dec 22/95
//
int daysInMonth(int month, int year)
{
	int validMonth = month;
	int validYear = year;
	int res, tempRes;
	while ( validMonth < 1 ) {
	    validMonth +=12;
	    validYear--;
	}
	while ( validMonth > 12 ) {
	    validMonth -=12;
	    validYear++;
	}
	res = ayear[validMonth] - ayear[validMonth-1];
	if (month == 2) {
	    res += addOne(12, validYear);
	}

	//  Having been burnt by this routine, let's add some
	//  extreme sanity checks!     Garance.
	tempRes = res;
	if ( res < 28 ) res = 28;
	if ( res > 31 ) res = 31;
	if ( res != tempRes ) {
	    fprintf(stderr, "Cassandra: daysInMonth(m=%d,y=%d) nearly "
			    "came up with result of %d! (will use %d)\n",
			    month, year, tempRes, res);
	}

	return( res ); 
}

//
// Returns how many days will be in that year
//
int daysInYear(int year)
{
	return (365 + addOne(12, year));
}


//
// Forgot
//
int addOne(int month, int year)
{
	if (month > 2) 
		return( !(year % 4) - !(year%100) + !(year%400));
	else
		return 0;
}

int daysUpTo( int month, int year)
{
	return(  ayear[month-1] + addOne(month, year)); 
}


int firstDayOf(int month, int year)
{	
	return ( (jan1(year) +   ayear[month-1] + 
			addOne(month, year) ) %7);
}
	

int wday(int day, int month, int year)
{	
	return(     ( firstDayOf(month, year)  + day -1)   % 7);
}


int  yday(int day, int month, int year)
{
	return(    daysUpTo(month, year) + day -1);
}


int printCalendar( int month, int year, NXStream *stream)
{
	int count, tmp, daysIn;
	
	NXPrintf(stream, "     %s %d\n",months[month -1], year);
	NXPrintf(stream, " S  M Tu  W Th  F  S\n");
	
	for( count = firstDayOf(month, year); count > 0; count --)
		NXPrintf(stream, "   ");
		
	for( count = 1; count < 8- firstDayOf(month, year); count ++)
		NXPrintf(stream, "%2d ", count);
	
	daysIn = daysInMonth(month, year);
	
	for(;;)
		{
		NXPrintf(stream, "\n");
		for(tmp =0; tmp < 7; tmp++)
			{
			NXPrintf(stream, "%2d ",count++);
			if( count > daysIn)
				return 1;
			}
		}
}

int printCalendarRTF( int day, int month, int year, NXStream *stream, 
			char *fontName, float fontSize)
{
	int count, tmp, daysIn;
	
	NXPrintf(stream, "{\\rtf0\\ansi{\\fonttbl\\f0\\fmodern %s;}\\fs%.0f\n",
			fontName, fontSize * 2);
	NXPrintf(stream, "{\\f0\\b      %s %d\\par}\n",months[month -1], year);
	NXPrintf(stream, "{\\f0\\b  S  M Tu  W Th  F  S\\par}\n");
	
	NXPrintf(stream, "{\\f0 ");
	
	for( count = firstDayOf(month, year); count > 0; count --)
		NXPrintf(stream, "   ");
		
	for( count = 1; count < 8- firstDayOf(month, year); count ++)
		if( count == day)
			NXPrintf(stream, 	"}\n"
						"{\\f0\\b\\i\\ul %2d}\n"
						"{\\f0  ", count);
		else
			NXPrintf(stream,"%2d ", count);

	
	daysIn = daysInMonth(month, year);
	
	for(;;)
		{
		NXPrintf(stream,	"\\par}\n"
				"{\\f0 ");
		for(tmp =0; tmp < 7; tmp++)
			{
			if( count == day)
				NXPrintf(stream, 	"}\n"
					"{\\f0\\b\\i %2d }\n"
					"{\\f0\\plain ", count++);
			else
				NXPrintf(stream,"%2d ", count++);
		
			if( count > daysIn)
				{
				NXPrintf(stream, "}\n");
				return 1;
				}
			}
		}
		
}

const char *
ascMyTime(struct tm * now, BOOL  showSeconds, BOOL militaryTime)
{
	static char string[64];
	int hour;
	char *timeMark;

	hour = now-> tm_hour;

	if( militaryTime)
		timeMark = "";
	else
		{
		if( hour > 11)
			timeMark = "pm";
		else
			timeMark = "am";
			
		if( hour  > 12)
			hour -= 12;
		if( hour == 0)
			hour = 12;
		}

	if( showSeconds)
		{
		sprintf(string, "%s %s %2d, %4d   %2d:%02d:%02d %s", 
			shortWeekDays[now->tm_wday],
			shortMonths[now->tm_mon], now->tm_mday,
			1900+now->tm_year, hour,
			now-> tm_min, now-> tm_sec, 
			timeMark);
		}
	else
		{
		sprintf(string, "%s %s %2d, %4d   %2d:%02d %s", 
			shortWeekDays[now->tm_wday],
			shortMonths[now->tm_mon], now->tm_mday,
			1900+now->tm_year, hour,
			now-> tm_min, 
			timeMark );
		}

	return( string);
}


const char *ascMyDate(struct tm * now)
{
	static char string[32];

	sprintf(string, "%s %s %2d, %4d", 
		shortWeekDays[now->tm_wday],
		shortMonths[now->tm_mon], now->tm_mday, 1900+now->tm_year);
	return( string);
}



int monthFromAscii(char *string)
{
	int a, atoi( char*);
	
	a = atoi( string);
	if( a <= 12 && a >= 1)	/* If string is a valid integer for a month */
		return a;
	
	for( a = 0; a < 12; a++)
		{
		if( calAsciiCmp( string, (char *) shortMonths[a]) == 0)
			return(a+1);
		}
	NXRunAlertPanel("Invalid Number", "Sorry, I could not recognize the "
			"month in the date you just typed. Could you please "
			"retype it? It should be a number from 1-12 or the "
			"name of a month, `Jan', `Feb', etc.",
			"OK",NULL,NULL);
	return 0;
}
	
	

int calAsciiCmp( char *a , char *b)		
{
	int index;
	char a1, b1;
	
	for( index = 0; index < 3; index ++)
		{
		a1 = * a++;
		b1 = * b++;
		
		if( isupper(a1))
			a1 = tolower( a1);
			
		if( isupper(b1))
			b1 = tolower(b1);
		
		if( a1 == b1)
			continue;
			
		if( a1 < b1)
			return -1;
		
		if( a1 > b1)
			return 1;
		}
	return 0;
}
	
//
// secondsBetween  - number of seconds between two date/times
//
double secondsBetween( struct tm * a, struct tm* b)
{
	double fromA, fromB;
	int 	flip, carryYear;

	#ifdef DEBUG
		fprintf(stderr,"Calculating the seconds between A <%s> ",
			ascMyTime( a, SEC, FALSE));
		fprintf(stderr," and B <%s>.\n", ascMyTime( b, SEC, FALSE));
	#endif


	/* Initialize values to 0 */
	fromA = fromB = 0.0;

	/* See if a is less than b, timeCompare does it, but */
	/* we need to negate its value */
	flip = - timeCompare( a, b);

	/* If the two times are the same, then return 0 seconds */
	if( flip == 0)
		return 0;

	/* If a is not less than b, than we need to flip a and b and */
	/* remember that at the very end */
	if( flip == -1)
		{
		struct tm temp;

		#ifdef DEBUG
			fprintf(stderr, "Flipping a and b.\n");
		#endif
	
		temp = *a;
		*a = *b;
		*b = temp;
		}
	
	/* Calculate the number of seconds from Jan 1st of <a>'s year */
	/* to <a> itself */
	fromA = (double) (yday(	a->tm_mday, a-> tm_mon+1, 
				a-> tm_year + 1900) * (24 * 60 * 60))
				+ (a-> tm_hour) * (60 * 60) 
				+ ( a-> tm_min * 60) + a-> tm_sec;

	/* Calculate the number of years seperating <a> from <b> */
	/* and add them to fromA */
	for( carryYear = a-> tm_year; carryYear < b-> tm_year; carryYear++)
		{
		#ifdef DEBUG
			fprintf(stderr,"Adding %d more days to fromB.\n",
				daysInYear(carryYear+1900));
		#endif
	
		fromB += (double) (daysInYear(carryYear+1900)	
			* (24 * 60 * 60));	/* times seconds in a day */
		}

	/* Calculate the number of secons from Jan 1st of */
	/* <b>'s year up to <b> itself */	
	fromB += (double) (yday(b->tm_mday, 
				b-> tm_mon+1, 
				b-> tm_year + 1900) * (24 * 60 * 60))
				+ (b-> tm_hour) * (60 * 60) 
				+ ( b-> tm_min * 60) + b-> tm_sec;

	#ifdef DEBUG
		fprintf(stderr, 
		"Seconds between %6.1f and %6.1f are %f (%6.1f min.).\n"
		"%d days.\n",
		fromA, fromB, (fromB - fromA)*flip, (fromB-fromA)*flip/60,
		(int) ((fromB - fromA)*flip) / ( 24*60*60) );
	#endif
	
	/* Return the difference of the two, also flipping the */
	/* minus sign if b happened to lesser */
	/* than a at the very beginning */
	return(    (fromB - fromA ) * flip);
}

//
// timeCompare  - compare two time structures
//
// return -1 if t1 is smaller than t2
// return 0 if they are the same
// return 1 if t1 is larger than t2

int timeCompare(struct tm *first, struct tm *second)
{
	double          t1, t2;

	t1 = (double) (first->tm_year * 10000 + first->tm_mon * 100 
			+ first->tm_mday);
	t2 = (double) (second->tm_year * 10000 + second->tm_mon * 100 
			+ second->tm_mday);

	#ifdef DEBUG
		fprintf(stderr, "Comparing Dates: %3.0f and %3.0f....\n",
				t1, t2);
	#endif

	if (t1 < t2)
		return -1;
	if (t1 > t2)
		return 1;

	t1 = first->tm_hour * 100 + first->tm_min;
	t2 = second->tm_hour * 100 + second->tm_min;
	
	#ifdef DEBUG
		fprintf(stderr, "Comparing Times: %3.0f and %3.0f....\n\n",
				t1, t2);
	#endif

	if (t1 < t2)
		return -1;
	if (t1 > t2)
		return 1;

	return (0);
}


//
// dayCompare  - compare two time structures
// 
// return -1 if t1 is smaller than t2
// return 0 if they are the same
// return 1 if t1 is larger than t2
int dayCompare(struct tm *first, struct tm *second)
{
	double          t1, t2;

	t1 = (double) (first->tm_year * 10000 + first->tm_mon * 100 
			+ first->tm_mday);
	t2 = (double) (second->tm_year * 10000 + second->tm_mon * 100 
			+ second->tm_mday);

	#ifdef DEBUG
		fprintf(stderr, "Comparing Dates: %3.0f and %3.0f....\n",
				t1, t2);
	#endif

	if (t1 < t2)
		return -1;
	if (t1 > t2)
		return 1;
	return (0);
}

void fixTmStructure( struct tm *ts)
{
	int temp;
	
	
	/* We must go around twice, first to check negative and 
	   then to check positive overflows */

	/* First check seconds */
	while( ts->tm_sec < 0)
		{
		ts->tm_sec += 60;
		ts->tm_min --;
		}
		
	/* Then minutes */
	while( ts->tm_min < 0)
		{
		ts->tm_min += 60;
		ts->tm_hour--;
		}
	
	/* Then hours */
	while( ts->tm_hour < 0)
		{
		ts -> tm_hour += 24;
		ts -> tm_mday --;
		}
	
	/* Then days (gets a bit more difficult now) */
	while( ts-> tm_mday < 1)
		{
		ts -> tm_mon --;
		if( ts-> tm_mon < 0)
			{
			ts-> tm_mon += 12;
			ts-> tm_year --;
			}
			
		ts -> tm_mday += daysInMonth( ts->tm_mon+1, ts->tm_year+ 1900);
		}
	
	/* Then months */
	if( ts-> tm_mon < 0 )
			{
			ts-> tm_mon += 12;
			ts-> tm_year --;
			}
			
	/* Then years */
	if( ts->tm_year < 0)
		ts->tm_year = 0;
		
		
	/* First check seconds */
	while( ts->tm_sec > 59)
		{
		ts->tm_sec -= 60;
		ts->tm_min ++;
		}
		
	/* Then minutes */
	while( ts->tm_min > 59)
		{
		ts->tm_min -= 60;
		ts->tm_hour++;
		}
	
	/* Then hours */
	while( ts->tm_hour > 23)
		{
		ts -> tm_hour -= 24;
		ts -> tm_mday ++;
		}
	
	/* Then days (gets a bit more difficult now) */
	temp = daysInMonth( ts->tm_mon +1, ts->tm_year+ 1900);
	while( ts-> tm_mday > temp)
		{
		ts -> tm_mday -= temp;
		ts -> tm_mon ++;
		if( ts-> tm_mon > 11)
			{
			ts-> tm_mon -= 12;
			ts-> tm_year ++;
			}
		temp = daysInMonth( ts->tm_mon +1, ts->tm_year+ 1900);
		}
	
	/* Then months (easy and last) */
	if( ts-> tm_mon > 11)
			{
			ts-> tm_mon -= 12;
			ts-> tm_year ++;
			}
	
	/* Fix up wday and yday and we're done! */
	ts->tm_wday = wday( ts->tm_mday, ts-> tm_mon + 1, ts-> tm_year + 1900);
	ts->tm_yday = yday( ts->tm_mday, ts-> tm_mon + 1, ts-> tm_year + 1900);

	/* All done, now we return :) */
	return;
}
	
const struct tm * timeNow()
{
	static struct tm time_now;
	struct tm *localtime();
	time_t time(), var;

	var = time(0);
	time_now = *localtime(&var); 
	return( &time_now);
}
