# From: Roger Burton West
# Subject: [SPOILER] Solution to Perl Quiz-of-the-Week #22
# Date: Sat, 28 Aug 2004 17:10:52 +0100
# X-Discordian-Date: Setting Orange, Bureaucracy 21 3170
# 
# On Wed, Aug 25, 2004 at 11:11:58PM -0400, Jose Alves de Castro wrote:
# 
# >This week's problem consists of writing the script 'upcoming', which
# >tells us about our upcoming events.
# 
# >As you'll notice, it's a little hard to schedule things such as the
# >fourth Thursday of each month, or dates like Mother's Day (I don't
# >know about the rest of the world, but that changes, here in Portugal).
# >It might be a good idea to find a reasonable way to solve this.
# 
# This sounds like a job for the Swiss Army Chainsaw of date modules,
# Date::Manip. Yes, it's not the world's fastest module, but it does get
# the job done eventually...
# 
# I decided to make this a demonstration for Date::Manip, so while the
# supplied format (with its American month-first strangeness) is
# supported, all the items found in the files are converted into
# Date::Manip's internal recurring-event format. This makes it easy to
# add other sorts of recurring event to the listings: you can either use
# the complex but powerful standard format:
# 
# 0:1*-1:2:0:0:0 Last Tuesday of the month
# 0:1*1:3:0:0:0/FD1 London.pm meeting (day after first Wednesday)
# 1*5:1:7:0:0:0 Mothers' Day in Portugal (first Sunday in May)
# 
# (see perldoc Date::Manip for details; flags are separated from the
# recurrence specification by a forward slash, and base date cannot be
# specified, in this implementation) or the more restricted English format
# (needs a colon separator):
# 
# 4th thursday of every month: something happens
# 
# Once the recurrence specification is constructed, the rest is easy:
# Date::Manip will give a list of events occurring in the date range, and
# these are interleaved with other events in the file to produce a
# chronological listing. This automatically takes care of the situation
# where more than one of the event occurs in the specified time period.
# 
# Behaviour in the case of invalid input is undefined.
# 
# 
#! /usr/bin/perl -w

use strict;
use Getopt::Std;
use Date::Manip;

my %o=(n => 7);
getopts('n:',\%o);
$o{n}++;

my $base=&ParseDate('1970-01-01 at midnight');
my $now=&ParseDate('today at midnight');
my $then=&DateCalc($now,"+ $o{n} days");

my $basedir="$ENV{HOME}/.upcoming";

opendir D,"$basedir";
my @files=sort grep !/^\./,readdir D;
closedir D;

foreach my $f (@files) {
  my @out;
  open F,"<$basedir/$f";
  while (<F>) {
    chomp;
    my ($recur,$event,$flags)=('')x3;
    if (/^   ([\d ][\d])\s+(.*)/) {
      (my $d,$event)=($1,$2);
      $d+=0;
      $recur="0:1*0:$d:0:0:0";
    } elsif (/^([\d ][\d])\/([\d ][\d])\/([\d ]{2}[\d]{2})\s+(.*)/) {
      (my $m,my $d,my $y,$event)=($1,$2,$3,$4);
      $d+=0;
      $m+=0;
      $y+=0;
      $recur="*$y:$m:0:$d:0:0:0";
    } elsif (/^([\d ][\d])\/([\d ][\d])\s+(.*)/) {
      (my $m,my $d,$event)=($1,$2,$3);
      $m+=0;
      $d+=0;
      $recur="1*$m:0:$d:0:0:0";
    } elsif (/^([-\d*:]+)(?:\/([A-Z\d]+))?\s+(.*)/) {
      ($recur,$flags,$event)=($1,$2,$3);
    } elsif (/^([a-z\d\s]+):\s+(.*)/i) {
      ($recur,$event)=($1,$2);
    }
    if ($recur && $event) {
      my @l=&ParseRecur($recur,$base,$now,$then,$flags);
      if (@l) {
        foreach my $e (@l) {
          my $d=int(&Delta_Format(&DateCalc($now,$e),0,'%dt')+.001);
          push @{$out[$d]},&leader($d)." $event\n"
        }
      }
    }
  }
  close F;
  if (@out) {
    print "$f\n";
    foreach my $daysworth (@out) {
      if (defined $daysworth) {
        print @{$daysworth};
      }
    }
    print "\n"
  }
}

sub leader {
  my ($n)=@_;
  my $k='';
  if ($n<=7) {
    $k=[
      '   ===>',  
      '   -->',
      '  -->',  
      ' -->',
      '-->',
      '->',
      '>',
      ''
    ]->[$n];
  } else {
    $k="($n)";
  }
  $k.=' ' x (7-length($k));
  return $k;
}
