#!/usr/bin/perl 

#$Id: eve,v 1.5 2004/11/29 13:19:39 gleb Exp $

use Time::localtime;
use Date::Calc qw(Delta_Days Decode_Month Month_to_Text check_date);
use Getopt::Long;
use strict;


=head1 NAME

eve is console events reminder for men. Women no need for it, 
they remember dates and events very well =)

=head1 SYNOPSIS

eve [OPTIONS] 

Options:
 -f <file>, --file=<file> file with events list for remind 
                            (default: ~/.eve)
   -a, --all  show all events (ignore notification rules)
   -c, --color  use ANSI colors 
   -v, --verbose  verbose mode 
   
   -h, --help  show this text 

=head1 DESCRIPTION

B<eve> is console reminder which shows description of future events. 
It may be used in ~/.bashrc file (with color mode) or in crontab
to get notification by mail.

=head1 OPTIONS

=over 4

=item B<-f> I<file> or B<--file=>I<file>

Use alternative remind list instead of ~/.eve

=item B<-a> or B<--all>

Show all future events from list (ignore notification rules) 

=item B<-c> or B<--color>

Use ANSI colors 

=item B<-v> or B<--verbose>

Verbose mode for debug

=item B<-h> or B<--help>

Show help  

=back


=head1 CONFIGURATION

File with events list for remind (default: ~/.eve) consists of lines
described below. Lines starting with '#' are comments.


=head2 Format of event's line

B<dd mon [notification_rule] Description of event>

=over 4

=item B<dd> (number)

Day of month 1-31

=item B<mon> (string)

Name of month - full english name or first 3 letters of it

=item B<notification rule> (list of numbers or number diapasons)

notification rule - (optional) default notification rule is [30, 20, 10, 5-0]
which mean to show remind on 30th, 20th, 10th day before this event and every 
day starting from fifth day to day zero
 
=item B<description of event> (string)

Text of notification

=back

=head2 Example of ~/.eve


24 December Marry Christmas 
31 dec [20, 10, 4, 1] Happy New Year


01 April Fools Day!

# this remind will be show during for 5th, 12th, 13th, 14th and 15th Jun
15 Jun [10, 3-0] John Doe's birthday! 


=head1 AUTHOR

Gleb Galkin E<lt>maniwheel@elnet.ruE<gt>

=head1 PREREQUISITES

This script requires the C<Date::Calc> and  C<Getopt::Long>


=pod OSNAMES

any

=pod SCRIPT CATEGORIES

UNIX/System_administration

=cut

######################################################################


my %colorize = (
    3  => "red",
	10 => "blue",
	30 => "green"
);

my $notification_rule_def = "[30, 20, 10, 5-0]";

my ($color, $today, $c_day, $c_mon, $c_year, $e_mon, $e_day, $delta, $string, $out);

$today = localtime(time);
($c_day, $c_mon, $c_year)  = ($today->mday, $today->mon+1, $today->year + 1900);



my $opt_file = $ENV{'HOME'} . "/.eve";
my $opt_all='';
my $opt_color='';
my $opt_v='';
my $opt_help='';

usage() unless GetOptions(
     "file|f=s"            => \$opt_file,
     "all|a"               => \$opt_all,
     "color|c"             => \$opt_color,
     "verbose|v"           => \$opt_v,
     "help|h|?"            => \$opt_help
);

usage() if $opt_help;


$opt_v && print "\n$0:\n-- verbose mode ON\n\n";
$opt_v && print "read file $opt_file\n";

open FILE, $opt_file || die "Can't open file $opt_file\n$!\n";

output ("\nreminders for $c_day-" . Month_to_Text($c_mon) . "-$c_year\n\n", "white");

my $i=0;
my $events=0;
while (<FILE>) {
   chomp; 
   $i++;
   undef($color);
   undef($string);
   $opt_v && print "$i: $_\n";
   m/^\s*#|^$/ && next;
   quit ("$opt_file: string number $i is invalid\n> $_ <\n")
        unless (/^(\d+)\s+(\w+)\s+(\[.*\])?\s*(.*)$/);

   $string = "$1 $2 - $4"; 
   my $notification_rule = $3 ? $3 : $notification_rule_def;

   quit ("$opt_file: string number $i contain invalid month\n> $_<\n     ^^^\n")
        unless ($e_mon = Decode_Month($2));
   $e_day = $1;

   quit ("$opt_file: string number $i contain invalid data\n> $_<\n  ^^^^^^\n") 
      unless (check_date($c_year, $e_mon, $e_day));

   quit ("$opt_file: string number $i contain invalid show days list\n> $3 <\n") 
      if ($3 && $3 !~ /^\[[0-9, -]+\]$/);
	  

   $delta = Delta_Days($c_year, $c_mon, $c_day, $c_year, $e_mon, $e_day);
   $delta = Delta_Days($c_year, $c_mon, $c_day, $c_year+1, $e_mon, $e_day) if ($delta < -180);
   my $check = check_show_day($delta, $notification_rule); 
   quit ("$opt_file: string number $i contain invalid show days list\n> $notification_rule <\n") 
    if ($check == -1);
   if ($check) {
      undef($color);
      foreach (reverse sort by_number keys %colorize) { 
	     if ($delta <= $_ && $delta > 0) { $color = $colorize{$_} }
	  } 
      if ($check==1) {
	     output ("$string (in $delta days)\n", $color);
		 $events++;
	  }	 
   }
}

print $out if ($events);


sub usage {
   print "
Usage: $0 [OPTIONS] 

Options:
   -f <file>, --file=<file> file with events list for remind 
                            (default: ~/.eve)
   -a, --all  show all events (ignore notification rules)
   -c, --color  use ANSI colors 
   -v, --verbose  verbose mode 
   
   -h, --help  show this text 
   
\n";
   exit;
}


sub output {
   my ($string, $color) = @_;
   my %colors = (white => "\033[1;37m", red => "\033[1;31m", blue => "\033[1;34m", green => "\033[1;32m");
   my $color_off ="\033[0m"; 
 
   $out .= $colors{$color} if $opt_color;
   $out .= $string;
   $out .= $color_off if $opt_color;

}

sub quit {
  print $_[0];
  exit;
}

sub check_show_day {
  my ($delta, $days) = @_;
  return 1 if ($opt_all);
  $days =~ s/[\[\] ]//g;
  my @array = split(/,/, $days);
  my ($from, $to);
  foreach (@array) {
     if (/(^\d+)-(\d+)$/) {
	    if ($1 > $2) { 
		   $from=$2;
		   $to=$1;
		}
		else {
		   $from=$1;
		   $to=$2;
		}
		return 1 if ($delta >= $from && $delta <= $to);
	 }
	 elsif (/^\d+$/) {
	    return 1 if ($delta==$_);
	 }
	 else {return -1}
  }
  return 0;
}

sub by_number { $a <=> $b }
