	/*** (C) Copyright 1986  S.R.I. International ***/
/*
 *
 *	Parse a date specification and return its internal representation
 *
 */
#include "vax-mm.h"
#include "date.h"
#include <time.h>

/*
 *	Parse the next tokens as a date and return the internal rep.
 */
int Get_Date()
{
	static struct {
		int current_size, maximum_size;
		struct tbluk_keyword keywords[9];
		} Day_Table = {
		9, 9,
#define	DAY_OF_WEEK	0
#define	DAY_OFFSET	1
	/* 0 */{{0,	"friday",		5 | (DAY_OF_WEEK << 16)},
	/* 1 */	{0,	"monday",		1 | (DAY_OF_WEEK << 16)},
	/* 2 */	{0,	"saturday",		6 | (DAY_OF_WEEK << 16)},
	/* 3 */	{0,	"sunday",		0 | (DAY_OF_WEEK << 16)},
	/* 4 */	{0,	"thursday",		4 | (DAY_OF_WEEK << 16)},
	/* 5 */	{0,	"today",		0 | (DAY_OFFSET  << 16)},
	/* 6 */	{0,	"tuesday",		2 | (DAY_OF_WEEK << 16)},
	/* 7 */	{0,	"wednesday",		3 | (DAY_OF_WEEK << 16)},
	/* 8 */	{0,	"yesterday",		1 | (DAY_OFFSET  << 16)}}};

	static struct {
		int current_size, maximum_size;
		struct tbluk_keyword keywords[3];
		} First_Last_Table = {
		3, 3,
#define	FIRST_MESSAGE		0
#define	LAST_MESSAGE		1
#define	LAST_LOGIN		2
	/* 0 */{{0,	"first",		FIRST_MESSAGE},
	/* 1 */	{0,	"last",			LAST_MESSAGE},
	/* 2 */	{0,	"login",		LAST_LOGIN}}};

	static struct {
		int current_size, maximum_size;
		struct tbluk_keyword keywords[3];
		} Login_Only_Table = {
		1, 1,
	/* 2 */	{{0,	"login",		LAST_LOGIN}}};

	static struct {
		int current_size, maximum_size;
		struct tbluk_keyword keywords[22];
		} Holidays = {
		22,22,
/*			Holiday Name		      Month  Day	 */
/*			------------		      -----  ---	 */
	/* 0 */{{0,	"april-fools",			4  + (1  << 16)},
	/* 1 */	{0,	"bastille-day",			7  + (14 << 16)},
	/* 2 */	{0,	"beethovens-birthday",		12 + (16 << 16)},
	/* 3 */	{0,	"bilbos-birthday",		9  + (22 << 16)},
	/* 4 */	{0,	"christmas",			12 + (25 << 16)},
	/* 5 */	{0,	"columbus-day",			10 + (12 << 16)},
	/* 6 */	{0,	"flag-day",			6  + (14 << 16)},
	/* 7 */	{0,	"frodos-birthday",		9  + (22 << 16)},
	/* 8 */	{0,	"gondorian-new-year",		3  + (25 << 16)},
	/* 9 */	{0,	"ground-hogs-day",		2  + (2  << 16)},
	/* 10*/	{0,	"guy-fawkes-day",		11 + (5  << 16)},
	/* 11*/	{0,	"halloween",			10 + (31 << 16)},
	/* 12*/	{0,	"independence-day",		7  + (4  << 16)},
	/* 13*/	{0,	"leap-day",			2  + (29 << 16)},
	/* 14*/	{0,	"lincolns-birthday",		2  + (12 << 16)},
	/* 15*/	{0,	"may-day",			5  + (1  << 16)},
	/* 16*/	{0,	"memorial-day",			5  + (30 << 16)},
	/* 17*/	{0,	"new-years",			1  + (1  << 16)},
	/* 18*/	{0,	"saint-patricks-day",		3  + (17 << 16)},
	/* 19*/	{0,	"sherlock-holmes-birthday",	1  + (6  << 16)},
	/* 20*/	{0,	"valentines-day",		2  + (14 << 16)},
	/* 21*/	{0,	"washingtons-birthday",		2  + (22 << 16)}}};

	static struct comnd_function f10 =
		{COMND_KEYWORD,
		 COMND_HELP_VALID,
		 0,
		 (int)&Holidays,
		 "holiday, "};
	static struct comnd_function f9 =
		{COMND_TOKEN,
		 (COMND_SUPPRESS_DEFAULT_HELP | COMND_HELP_VALID),
		 &f10,
		 (int)"#",
		 "\"#\" followed by a message number to use the receive date for that message"};
	static struct comnd_function f8 =
		{COMND_TOKEN,
		 (COMND_SUPPRESS_DEFAULT_HELP | COMND_HELP_VALID),
		 &f9,
		 (int)"%",
		 "\"%\" to use the receive date of the last message"};
	static struct comnd_function f7 =
		{COMND_TOKEN,
		 (COMND_SUPPRESS_DEFAULT_HELP | COMND_HELP_VALID),
		 &f8,
		 (int)"*",
		 "\"*\" to use the receive date of the last message"};
	static struct comnd_function f6 =
		{COMND_TOKEN,
		 (COMND_SUPPRESS_DEFAULT_HELP | COMND_HELP_VALID),
		 &f7,
		 (int)"-",
		 "\"-\" followed by number of days in the past"};
	static struct comnd_function f5 =
		{COMND_KEYWORD,
		 COMND_HELP_VALID,
		 &f6,
		 (int)&First_Last_Table,
		 ""};
	static struct comnd_function f4 =
		{COMND_KEYWORD,
		 COMND_HELP_VALID,
		 &f5,
		 (int)&Day_Table,
		 ""};
	static struct comnd_function f3 =
		{COMND_DATE,0,&f4,COMND_DO_TIME};
	static struct comnd_function f2 =
		{COMND_DATE,0,&f3,COMND_DO_DATE};
	static struct comnd_function f1 =
		{COMND_DATE,0,&f2,COMND_DO_DATE|COMND_DO_TIME};
	int i;
	int t;
	struct tm *tm;
	struct comnd_function *f;

	/*
	 *	Parse the date token
	 */
    	if (Last_Message == 0) {
    	    f5.data = (int)&Login_Only_Table;
    	} else {
    	    f5.data = (int)&First_Last_Table;
    	}
	COMND(&Command_State,&f1,&i,&f);
	/*
	 *	Day of week name or today/tomorrow?
	 */
	if (f == &f4) {
		/*
		 *	Yes: Get the data from the keyword entry
		 *	     (and the local time -- we will need it later)
		 */
		i = ((struct tbluk_keyword *)i)->user_data;
		t = time(0);
		tm = localtime((unsigned long *)&t);
		/*
		 *	Dispatch on operation
		 */
		switch(i >> 16) {
			/*
			 *	Day of the week
			 */
			case DAY_OF_WEEK:
				/*
				 *	Extract the day of the week (Sun = 0)
				 */
				i &= 0xffff;
				/*
				 *	Now calculate the number of days to go
				 *	back to get the desired day.
				 */
				if (i <= tm->tm_wday) {
					i -= tm->tm_wday;
				} else {
					i = i - tm->tm_wday - 7;
				}
				/*
				 *	Calculate the number of seconds
				 */
				i *= (24*60*60);
				/*
				 *	Plus number of seconds required to
				 *	get back to 12:00:01AM.
				 */
				i -= (tm->tm_hour * (60 * 60));
				i -= (tm->tm_min * 60);
				i -= (tm->tm_sec - 1);
				/*
				 *	Return the appropriate time
				 */
				return(t+i);
			/*
			 *	Today/Tomorrow ...etc
			 */
			case DAY_OFFSET:
				/*
				 *	Extract the number of days to offset
				 */
				i &= 0xffff;
				/*
				 *	Calculate the number of seconds
				 */
				i *= (24*60*60);
				/*
				 *	Plus number of seconds required to
				 *	get back to 12:00:01AM.
				 */
				i += (tm->tm_hour * (60 * 60));
				i += (tm->tm_min * 60);
				i += (tm->tm_sec - 1);
				/*
				 *	Return the appropriate time
				 */
				return(t - i);
		}
	}
	/*
	 *	First/Last/Login?
	 */
	if (f == &f5) {
		/*
		 *	Get the operation from the keyword data
		 */
		i = ((struct tbluk_keyword *)i)->user_data;
		switch(i) {
			/*
			 *	Use the date of the 1st message
			 */
			case FIRST_MESSAGE:
				return(Messages[0].Date);
			/*
			 *	Use the date of the last message
			 */
			case LAST_MESSAGE:
				return(Messages[Last_Message-1].Date);
			/*
			 *	Use the date of our last login
			 */
			case LAST_LOGIN:
				return(Last_Login());
		}
	}
	/*
	 *	"-" followed by number of days in the past?
	 */
	if (f == &f6) {
		static struct comnd_function number = {COMND_NUMBER,0,0,10};

		/*
		 *	Parse the number of days in the past
		 */
		COMND(&Command_State,&number,&i,0);
		/*
		 *	Calculate the number of seconds to get to 12:00:01AM
		 *	on that day.
		 */
		t = time(0);
		tm = localtime((unsigned long *)&t);
		i *= (24*60*60);
		i += (tm->tm_hour * (60 * 60));
		i += (tm->tm_min * 60);
		i += (tm->tm_sec - 1);
		/*
		 *	Return the appropriate time
		 */
		return(t - i);
	}
	/*
	 *	Use the date of the last message?
	 */
	if (f == &f7) return(Messages[Last_Message-1].Date);
	if (f == &f8) return(Messages[Last_Message-1].Date);
	/*
	 *	Use the date of the indicated message?
	 */
	if (f == &f9) {
		static struct comnd_function number = {COMND_NUMBER,0,0,10};

		/*
		 *	Parse the message number to use
		 */
		COMND(&Command_State,&number,&i,0);
		/*
		 *	Make sure it is in range
		 */
		if ((i <= 0) || (i > Last_Message)) {
			printf("\n?Out of range.\n");
			return(0);
		}
		/*
		 *	Return the appropriate date
		 */
		return(Messages[i-1].Date);
	}
	/*
	 *	A holiday
	 */
	if (f == &f10) {
		struct tops_date d;
		int Time[2];

		/*
		 *	Get the encoded month/day
		 */
		i = ((struct tbluk_keyword *)i)->user_data;
		/*
		 *	Get the current year/timezone
		 */
		idtnc_jsys("now",0,&d);
		/*
		 *	Set the time to 12:00:01AM.
		 */
		d.hour = 0;
		d.minute = 0;
		d.second = 1;
		/*
		 *	Do we have to go to LAST year to get the holiday?
		 */
		if (d.month <= (i & 0xffff)) {
			if ((d.month < (i & 0xffff)) ||
			    (d.day < ((i >> 16) & 0xffff))) {
				d.year--;
			}
		}
		/*
		 *	Set the Day/Month
		 */
		d.month = i & 0xffff;
		d.day = (i >> 16) & 0xffff;
		/*
		 *	Convert to internal format
		 */
		idcnv_jsys(&d,Time);
		/*
		 *	Return it
		 */
		return(Time[0]);
	}
	/*
	 *	Normal Date/Time -- just return the time
	 */
	return(i);
}

