package Apache::LowCal::LowCal;

# Apache::LowCal::LowCal - A lightweight web calendar
# Copyright (C) 2000 Dave Sherohman (dave@sherohman.org)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

use strict;

use Apache::Constants qw (:common);
#use Apache::File ();
use Apache::Util qw(escape_uri);

use Time::Local;

use Apache::LowCal::Common qw(&parse_template &parse_date);

my $version = "1.1.0";
use vars '$uri';
my %args = ();
my %tokens = ('VERSION', $version);
my @categories = ();

# Module config settings
my ($event_path, $daily_links);

# Make some lists of month and day names for future reference
my @month_abbr = ('', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                   'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
my @month_name = ('', 'January', 'February', 'March', 'April', 'May', 'June',
                  'July', 'August', 'September', 'October', 'November',
                  'December');
my @day_abbr = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
my @day_name = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
                'Friday', 'Saturday');

sub handler {
  my $r = shift;

  $ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';

  # Read config options
  $event_path = $r->server_root_relative($r->dir_config('EventPath'));
  chdir $event_path || die "Bad EventPath: $event_path ($!)\n";
  $daily_links = $r->server_root_relative($r->dir_config('DailyLinks')) || 0;

  my @allargs = ($r->args, $r->content);
  %args = ();   # Clear out any data left over from previous calls
  while (my ($argname, $argval) = splice @allargs, 0, 2) {
    if (exists $args{$argname}) { $args{$argname} .= ',' . $argval }
    else                        { $args{$argname}  = $argval }
  }

  $r->uri =~ m~.*/([^/]+)/?$~;
  $uri = $1;
  $tokens{URI} = $uri;

  my ($mday, $nmon, $year, $wday)
    = parse_date($args{'date'}, $args{'day'}, $args{'month'}, $args{'year'});

  parse_categories($r);

  set_base_tokens($year, $nmon, $mday, $wday);

  if ($args{'event'}) {
    $args{'event'} =~ /([^`' "]+)/;
    my $event = $1;

    $tokens{EVENTNAME} = $args{'eventname'};

    return show_event($r, $event);
  } elsif ($args{'listdays'}) {
    $args{'listdays'} =~ /([0-9]+)/;
    return list_days($r, $year, $nmon, $mday, $1, 1);
  } elsif ($args{'summarydays'}) {
    $args{'summarydays'} =~ /([0-9]+)/;
    return list_days($r, $year, $nmon, $mday, $1, 0);
  } else {    # Default to monthly calendar display
    return show_month($r, $year, $nmon);
  }
}

sub show_month {
  my ($r, $year, $nmon) = @_;

  my $cal = `cal $nmon $year`;    # Should remove this dependence on cal
  my @weeks = split "\n", $cal;
  shift @weeks; shift @weeks;  # Strip off two header lines from cal's output

  $r->content_type('text/html');
  $r->send_http_header;
  return OK if $r->header_only();

  # Get list of files in event directory
  opendir(EVENTS, '.') || die "Can't open directory: $!";
  my @events = sort readdir EVENTS;
  closedir EVENTS;
  my $catlist = join ',', @categories;

  # Send page header
  print("<html><head>\n<title>LowCal Calendar</title>\n</head><body>\n")
    unless (parse_template($r, "cal_header", \%tokens) == OK);

  # Display calendar itself
  print("<table border=1 align=center width=95%>\n");
  print("<tr valign=top>\n");
  foreach (@day_name) {
    print("<td align=center><b>$_</b></td>");
  }
  print("</tr>\n");

  foreach my $week (@weeks) {
    my @days = split ' ', $week;

    print("<tr valign=top>\n");

    # Pad @days with leading blanks if it's the first week
    if ($days[0] == 1) {
      foreach (0 .. (6 - $days[-1])) {
        print("<td>&nbsp;</td>");
      }
    }

    foreach my $day (@days) {
      if ($day + 0 == 0) {
        # Remove this clause in 1.1.1 if the NONNUMERIC warning isn't seen
        print("<td align=center width=14%><b>NONNUMERIC - $day</b></td>");
      } else {
        print("<td width=14%><p align=right>");
        if ($daily_links) {
          print("<a href=\"$uri?date=$nmon/$day/$year&listdays=1");
          print("&category=$catlist") if $catlist;
          print "\">$day</a></p>";
        } else {
          print("$day</p>");
        }
        my $datestr = sprintf "%04d%02d%02d", $year, $nmon, $day;
        foreach (grep /^$datestr/, @events) {
          my $evtid = $_;
          if (open(EVTFILE, $evtid)) {
            while (<EVTFILE>) {
              if (/<meta .*eventname=\"([^\"]*)\"/i) {                #"
                my $evtname = $1;
                my $category = /category=\"([^\"]*)\"/i ? $1 : "";    #"
                my $location = /location=\"([^\"]*)\"/i ? $1 : "";    #"
                if (display_category($r, $category)) {
                  if ($location) {
                    print "<a href=\"$location\">$evtname</a><p>";
                  } else {
                    my $escaped = escape_uri($evtname);
                    print("<a href=\"$uri?event=$evtid&date=$nmon/$day/$year"
                        . "&eventname=$escaped");
                    print("&category=$catlist") if $catlist;
                    print("\">$evtname</a><p>");  
                  }
                }
                last;
              }
            }
            close EVTFILE;
          } else {
            print("$evtid: $!");
          }
        }
        print("</td>");
      }
    }

    print("</tr>\n");
  }
  print("</table>\n\n");

  # Send page footer
  print("</body></html>")
    unless (parse_template($r, "cal_footer", \%tokens) == OK);

  return OK;
}

sub show_event {
  my ($r, $event) = @_;

  $r->content_type('text/html');
  $r->send_http_header;
  return OK if $r->header_only();

  # Send event header
  print("<html><head>\n<title>LowCal Calendar</title>\n</head><body>\n")
    unless (parse_template($r, "evt_header", \%tokens) == OK);

  # Send event body
  print("Error:  Event file $event has disappeared!<p>")
    unless (parse_template($r, $event, \%tokens) == OK);

  # Send event footer
  print("</body></html>")
    unless (parse_template($r, "evt_footer", \%tokens) == OK);

  return OK;
}

sub list_days {
  my ($r, $startyear, $startmon, $startday, $ndays, $details) = @_;

  $r->content_type('text/html');
  $r->send_http_header;
  return OK if $r->header_only();

  my $sec_in_day = 86400; # = 60 * 60 * 24
  my $epochtime = timelocal(0, 0, 12, $startday, ($startmon - 1), $startyear);
  my $endtime = $epochtime + ($sec_in_day * ($ndays - 1));
  my $multiple_days = $ndays > 1;

  # Get list of files in event directory
  opendir(EVENTS, '.') || die "Can't open directory: $!";
  my @events = sort readdir EVENTS;
  closedir EVENTS;
  my $catlist = join ',', @categories;

  # Start date tokens
  my ($startday, $startmon, $startyear, $startwday)
    = (localtime($epochtime))[3..6];
  $startmon += 1;
  $startyear += 1900;

  $tokens{STARTYEAR} = $startyear;

  $tokens{STARTMONTH} = $startmon;
  $tokens{STARTMONTHABBR} = $month_abbr[$startmon];
  $tokens{STARTMONTHNAME} = $month_name[$startmon];

  $tokens{STARTDAY} = $startday;
  $tokens{STARTDAYABBR} = $day_abbr[$startwday];
  $tokens{STARTDAYNAME} = $day_name[$startwday];

  # End date tokens
  my ($endday, $endmon, $endyear, $endwday) = (localtime($endtime))[3..6];
  $endmon += 1;
  $endyear += 1900;

  $tokens{ENDYEAR} = $endyear;

  $tokens{ENDMONTH} = $endmon;
  $tokens{ENDMONTHABBR} = $month_abbr[$endmon];
  $tokens{ENDMONTHNAME} = $month_name[$endmon];

  $tokens{ENDDAY} = $endday;
  $tokens{ENDDAYABBR} = $day_abbr[$endwday];
  $tokens{ENDDAYNAME} = $day_name[$endwday];

  $tokens{DATERANGE} = $multiple_days 
      ? "$startmon/$startday/$startyear - $endmon/$endday/$endyear"
      : "$startmon/$startday/$startyear";

  # Send list header
  print("<html><head>\n<title>LowCal Event List</title>\n</head><body>\n")
    unless (parse_template($r, "list_header", \%tokens) == OK);

  for ( ; $ndays > 0 ; $ndays--) {
    my ($mday, $nmon, $year, $wday) = (localtime($epochtime))[3..6];
    $nmon += 1;
    $year += 1900;
    my $datestr = sprintf "%04d%02d%02d", $year, $nmon, $mday;

    my $first_event = 1;
    my ($evtname, $escaped, $category, $location);
    foreach (grep /^$datestr/, @events) {
      my $evtid = $_;

      if (open(EVTFILE, $evtid)) {
        while (<EVTFILE>) {
          if (/<meta .*eventname=\"([^\"]*)\"/i) {                #"
            $evtname = $1;
            $category = /category=\"([^\"]*)\"/i ? $1 : "";    #"
            $location = /location=\"([^\"]*)\"/i ? $1 : "";    #"
            $escaped = escape_uri($evtname);
            last;
          }
        }
        close EVTFILE;
      } else {
        print("$evtid: $!");
      }

      if (display_category($r, $category)) {
        if ($first_event) {
          $tokens{DATE} = "$nmon/$mday/$year";
          $tokens{EDATE} = "$mday-$nmon-$year";

          $tokens{YEAR} = $year;

          $tokens{MONTH} = $nmon;
          $tokens{MONTHABBR} = $month_abbr[$nmon];
          $tokens{MONTHNAME} = $month_name[$nmon];

          $tokens{DAY} = $mday;
          $tokens{DAYABBR} = $day_abbr[$wday];
          $tokens{DAYNAME} = $day_name[$wday];

          if ($multiple_days) {
            print("<hr>\n<h3>$nmon/$mday/$year -</h3>\n")
              unless (parse_template($r, "list_day_sep", \%tokens) == OK);
          }
  
          $first_event = 0;
        } elsif ($details) {
          print("<hr width=50%>\n")
            unless (parse_template($r, "list_evt_sep", \%tokens) == OK);
        }

        if ($details) {
          # Send event body
          if ($location) {
            print "<a href=\"$location\">$evtname</a><p>";
          } else {
            print("Error:  Event file $evtid has disappeared!<p>")
              unless (parse_template($r, $evtid, \%tokens) == OK);
          }
        } else {
          if ($location) {
            print "<a href=\"$location\">$evtname</a><p>";
          } else {
            print("<a href=\"$uri?event=$evtid&date=$nmon/$mday/$year"
                    . "&eventname=$escaped");
            print("&category=$catlist") if $catlist;
            print("\">$evtname</a><p>");  
          }
        }
      }
    }

    $epochtime += $sec_in_day;
  }

  # Send list footer
  print("<hr>\n</body></html>")
    unless (parse_template($r, "list_footer", \%tokens) == OK);

  return OK;
}

sub parse_categories {
  my $r = shift;

  my $catlist;
  if ($args{'category'}) {
    $catlist = $args{'categories'}
              ? $args{'category'} . ',' .  $args{'categories'}
              : $args{'category'};
  } elsif ($args{'categories'}) { 
    $catlist = $args{'categories'};
  } else {
    $catlist = '';
  }

  $catlist = '' if $catlist =~ /\*/;    # Empty string and * are equivalent

  @categories = split ',', $catlist;

  return;
}

sub display_category {
  my $r = shift;
  my $category = shift;

  # If category list is empty, display everything
  return 1 unless $#categories > -1;

  foreach my $cat (split ',', $category) {
    return 1 if (grep /$cat/i, @categories);
  }

  return 0;
}

sub set_base_tokens {
  my ($year, $nmon, $mday, $wday) = @_;

  $tokens{DATE} = "$nmon/$mday/$year";
  $tokens{EDATE} = "$mday-$nmon-$year";

  $tokens{YEAR} = $year;

  $tokens{MONTH} = $nmon;
  $tokens{MONTHABBR} = $month_abbr[$nmon];
  $tokens{MONTHNAME} = $month_name[$nmon];

  $tokens{DAY} = $mday;
  $tokens{DAYABBR} = $day_abbr[$wday];
  $tokens{DAYNAME} = $day_name[$wday];

  my ($omon, $oyear);

  if ($nmon == 1)   { $omon = 12;         $oyear = $year - 1; }
  else              { $omon = $nmon - 1;  $oyear = $year; }
  $tokens{LASTMONTH} = $omon;
  $tokens{LASTMONTHYEAR} = $oyear;
  $tokens{LASTMONTHABBR} = $month_abbr[$omon];
  $tokens{LASTMONTHNAME} = $month_name[$omon];

  if ($nmon == 12)  { $omon = 1;          $oyear = $year + 1; }
  else              { $omon = $nmon + 1;  $oyear = $year; }
  $tokens{NEXTMONTH} = $omon;
  $tokens{NEXTMONTHYEAR} = $oyear;
  $tokens{NEXTMONTHABBR} = $month_abbr[$omon];
  $tokens{NEXTMONTHNAME} = $month_name[$omon];

  $tokens{CATLIST} = ($#categories > -1) ? join ', ', @categories
                                         : 'all events';
  $tokens{CATPARAM} = join ',', @categories;
}

1;

