#/usr/local/bin/perl 

require 'date.pl';

$command = '';
print "	Date Calculator version 1.0\n";
print "	   (type `h' for help)\n";
print "> ";

while(<stdin>) {
	($command) = /^\s*(\w+)\s*$/;
	last if (index("quit",$command) == 0);
	if (/^\s*(\d+)\s+(\d+)\s+(\d+)\s*$/) {			# quit
		$j = &jday($1,$2,$3);
		push(@stack,$j);
		next;
	}
	elsif (/^\s*(\w+)\s+(\d+)(\s+(\d+)?)\s*$/) {	# mmm dd yy
		# assumes this year if year is missing
		$j = &jday(&monthnum($1),$2,$4);
		push(@stack,$j);
		next;
	}
	elsif (/^\s*([-]?\d+)\s*$/) {					# [-]n
		push(@stack,$1);
		next;
	}
	elsif (index("clear",$command)==0) {			# clear
		@stack = ();
		next;
	}
	elsif (index("duplicate",$command)==0) {		# duplicate
		push(@stack,$stack[$#stack]);
		next;
	}
	elsif (index("exchange",$command)==0 ||
	       $command eq 'x') {						# exchange
		$x = pop(@stack);
		$y = pop(@stack);
		push(@stack,$x);
		push(@stack,$y);
		next;
	}
	elsif (index("print",$command)==0) {			# print
		do print($stack[$#stack]);
		next;
	}
	elsif (index("today",$command)==0) {			# today
		push(@stack,&today());
		do print($stack[$#stack]);
		next;
	}
	elsif (/^\s*[+]\s*$/) {							# add
		$y = pop(@stack);
		$x = pop(@stack);
		if (&is_jday($x) && &is_jday($y)) {
			print stderr "** cannot add two dates\n";
			push(@stack,$x);
			push(@stack,$y);
			next;
		}
		$r = $x + $y;
		push(@stack,$r);
		do print($r);
		next;
	}
	elsif (m:^\s*([\-*/%])\s*$:) {					# (-) (*) (/) and (%)
		$y = pop(@stack);
		$x = pop(@stack);
		$r = eval "$x $+ $y";
		warn "** evaluation error $@\n" if $@ ne "";
		push(@stack,$r);
		do print($r);
		next;
	}
	elsif (index("Print",$command)==0) {				# dump
		do dump();
		next;
	}
	elsif (index("help",$command)==0) {					# help
		print <<EOD ;
Commands:

	mmm dd		Push date for current year onto stack
	mmm dd yyyy	Push date onto stack
	n or -n		Push positive/negative constant or interval onto stack
	+ - * / %	Add, subtract, multiply, divide, modulo
	expr		Push result of Perl expression onto stack
	<d>uplicate	Push a duplicate of the top value onto the stack
	<c>lear		Clear stack
	<p>rint		Print last value on stack
	<P>rint		Print all stack values
	<t>oday		Put today's date on the stack
	e<x>change	Exchange top two values of stack
	<q>uit		Exit the program

Note:   expressions are scanned for embedded dates of the form `1991/Jan/2',
        `Jan 1, 1991' or just `Jan 1'.  These dates are translated to Julian
        Day numbers before the expression is evaluated.  Also, the tokens
        `today', `tomorrow' and `yesterday' are replaced with their
        respective Julian Day numbers.  If the expression does something
        stupid with Julian Day numbers (like add them) you get silly
        results.
EOD
		next;
	}
	else {
		chop;
		# replace yyyy/mmm/dd dates with Julian day number
		  s|(\d{1,4})\W?(\w\w\w)\W?(\d\d?)|&jday(&monthnum($2),$3,$1)|ge;
		# replace mmm dd yyyy dates with Julian day number
		  s|(\w\w\w)[\W\s](\d\d?)[,]?[\W\s](\d{1,4})|&jday(&monthnum($1),$2,$3)|ge;
		# replace mmm dd dates with Julian day number (for this year)
		  s|(\w\w\w)[\W\s](\d\d?)|&jday(&monthnum($1),$2)|ge;
		# replace 'today' with todays jday
		  s|\b(today)\b|&today()|ge;
		# replace 'tomorrow' with tomorrows jday
		  s|\b(tomorrow)\b|&tomorrow()|ge;
		# replace 'yesterday' with yesterdays jday
		  s|\b(yesterday)\b|&yesterday()|ge;
		print $_,"\n";
		push(@stack,eval($_));
		do print($stack[$#stack]);
		next;
	}
#	else { warn "** invalid command - try \"help\"\n" unless ($_ eq "\n"); }
} continue {
	print "> ";
	$command = "";
}

sub print #(value)
{
	if (&is_jday($_[0])) {
		($m,$d,$y,$wd) = &jdate($_[0]);
		$month = &monthname($m,3);
		$wkday = &weekday($wd);
		print "= $wkday $month $d, $y (JD = $_[0])\n";
	} else {
		if ($_[0] > 365 || $_[0] < -365) {
			$years = int($_[0] / 365.25);
			$days = $_[0] - int($years * 365.25);
			print "= $_[0] days  ($years years, $days days)\n\n";
		} else {
			print "= $_[0] days\n\n";
		}
	}
}

sub dump
{
	for ($i = 0; $i <= $#stack; $i++) {
		print "stack[",$i,"] ";
		do print($stack[$i]);
	}
}