#!/usr/bin/perl

# Number of days to look ahead
$lookahead = shift || 4;

# Where the birthday database is
$database = "/home/set/addressbook/cumpleaños";

# Here be dragons below...

# This is a re-write, since I lost the original when I formatted and
# installed Fedora Core 2.  This is *a lot* faster than the original,
# since we pre-hash all of the birthdays.  It also has a fix, albeit
# ugly, for the 2/29 birthday bug the last version had.

# We actually use functions in this version, which makes the code cleaner

# Routine to convert dates to some more human friendly
# Input: A UNIX time stamp
# Output: A three-element list with the normal year, month, and day
sub convert {
	my($a,$b,$c,$day,$mon,$year) = localtime(shift);
	$mon++;
	$year += 1900;
	return ($year,$mon,$day);	
	}

# Routine to convert the output of "convert" in to a hash index
sub dindex {
	my($y) = shift;
	my($m) = shift;
	my($d) = shift;
	return sprintf("%02d.%02d",$m,$d);
	}
	
# Given $n, a person's name, $c, the number of days when they have a birthday
# (if c is 0, today's their lucky day), and $q, their (possible) contact info,
# make a human-readable string of who has a birthday when
sub output {
	my($n) = shift;
	my($c) = shift;
	my($d) = shift;
	my($o);
	while($n =~ /\s$/) {
		$n =~ s/\s$//g;
		}
	if($c >= 2) {
		$o = "$n has a birthday in $c days\n";
		}
	elsif($c == 1) {
		$o = "$n has a birthday tomorrow.\n";
		}
	elsif($c == 0) {
		$o = "Today is $n\'s birhtday!\n";
		}
	return $o;
	}

# Given a string that tells us who has a birthday when (in human-reabable
# format, inform the user of this fact.  We modularize this way so that
# we can both output to standard output, or send out mail, or IM them, or
# what not
sub inform {
	my($o) = shift;
	print $o;
	}

# main()

# First, open up the database

open(B,"< $database") || die "Can\'t open $database: $!\n";

# Make a hash, d, which has, with the day of year in MM.DD format as the
# index, the line number of everyone who has a birthday on that day.  This
# hash should speed up execution time of the program.

while(<B>) {
	$num++;
	$line = $_;
	chop($line);
	$line =~ s/\#.*//g;
	# We only process lines with XXXX.01.23 or what not in them;
	# other lines are simply ignored
	if($line =~ /\w\w\w\w\.(\d\d\.\d\d)/) {
		$day = $1;

		# Ugly hack to handle people born on 2/29: We'll just
		# always celebrate their birthdays on the 28th
		if($day =~ /02\.29/) { $day = "02.28"; }

		# We put the reference to the actual data in the "l" hash
		# in the "d" hash.  "l" is indexed by line number; "d" is
		# indexed by the day they were born.  An entry in the "d"
		# hash looks like "2" or "2,19" or "2,19,83"; the numbers
		# are separated by commas; 2, 19, and 83 are line numbers
		# of where the real data about the people are.
		if($d{$day}) { $d{$day} .= "," . $num; }
		else { $d{$day} = $num; }

		# OK, we put references in the "d" hash; put the real data 
		# in the "l" hash.
		$l{$num} = $line;
		}
	}

# Now, who has a birthday coming up soon?
$t = time();
$o = "";
for($c = 0; $c <= $lookahead; $c++) {	

	# We convert the day we are looking at in to a form like "02.19"
	$s = dindex(convert($t));	

	# If anyone has a birthday soon...
	if($d{$s}) {

		# Make a list of the line numbers of who has a birthday	on
		# the day we are looking at
		my(@p) = split(/\,/,$d{$s});

		# For each of those line numbers
		foreach $a (@p) {

			# Get the raw data for that person
			my($l);
			$l = ($l{$a});

			# And process the data in question
			if($l =~ /^(.*)\w\w\w\w\.\d\d\.\d\d\s+(.*)\s*$/) {
				$n = $1;
				$q = $2;
				$o .= output($n,$c,$q);
				}

			}
		# We now remove this element from the "d" (sorted by bday) hash
		# This means we can only look a year ahead for birthdays
		delete($d{$s});
		}
	$t += 86400; # 86400 seconds: One day; we look at the next day now
	}

# Finally, inform the user who has a birthday
inform($o);

