#!/usr/bin/perl -Tw
#
# movie2html - make a nice HTML table thingy
#
# $Id: movie2html,v 1.24 2005/01/11 15:42:34 esm Exp $
#
(our $ME = $0) =~ s|.*/||;
(our $VERSION = '$Revision: 1.24 $ ') =~ tr/[0-9].//cd;

use strict;
use CGI			qw(:standard);

###############################################################################
# BEGIN user-customizable section

our $MLIST = "movies.source";				# Movie list
our $URL   = "http://us.imdb.com/M/title-exact?";	# URL to point to

our $IN    = "film-index.html";		# input (template) file
our $OUT   = "index.html";		# output file

#
# These are not expected to change any time soon
#
our @MoY = ("January","February","March",     "April",   "May",      "June",
	"July",   "August",  "September", "October", "November", "December");


# Running total
our %Total;

# END   user-customizable section
###############################################################################

###############################################################################
# BEGIN boilerplate args checking, usage messages

END {
  use POSIX qw(_exit);
  # This is required if the code might send any output to stdout
  # E.g., even --version or --help.  So it's best to do it unconditionally.
  close STDOUT
    or (warn "$ME: closing standard output: $!\n"), _exit (1);
}

sub usage($) {
  my ($exit_code) = @_;
  my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
  if ($exit_code != 0) {
    print  $STREAM "Try `$ME -help' for more information.\n";
  } else {
    print  $STREAM <<EOH;
Usage: $ME [OPTIONS]

blah blah blah

OPTIONS:

  --dry-run    make no changes, but display new crontab to stdout
  --help       display this message
  --version    display program name and version
EOH
  }

  exit $exit_code;
}

# Command-line options
my $debug   = 0;
my $dry_run = 0;
my $force   = 0;
use Getopt::Long;
GetOptions( 'debug!'     => \$debug,
	    'dry-run|n!' => \$dry_run,
	    force        => \$force,
	    help         => sub { usage 0 },
	    version      => sub { print "$ME version $VERSION\n"; exit 0 },
) or usage(1);

usage(1) if @ARGV;

# END   boilerplate args checking, usage messages
###############################################################################

###############################################################################
# BEGIN helper subs


sub push_onto($$@);
sub push_onto($$@) {
  my ($href, $movie, @categories) = @_;

  my $cat = shift @categories;

  if (@categories) {
    $href->{$cat}->{sub} ||= {};
    push_onto $href->{$cat}->{sub}, $movie, @categories;
  } else {
    $movie =~ /^\s*(.*?)\s+\((\d+)\)(\s+\[VHS\])?$/
      or die "$ME: Unparseable line, '$movie'\n";
    $href->{$cat}->{movies} ||= [];
    push @{$href->{$cat}->{movies}}, { name  => $1,
				       year  => $2,
				       class => (defined $3 ? 'VHS' : 'DVD') };
  }
}

sub read_movies() {
  my %list;
  my @categories;

  open MLIST, '<', $MLIST
    or die "$ME: open( $MLIST ): $!\n";
  while (defined (my $line = <MLIST>)) {
    chop $line;
    $line =~ s!\#.*$!!;			# Remove '#' comments
    $line =~ s!\s+$!!;			# trim trailing, but not leading, w.s.
    $line or next;			# skip blank lines

    if ($line =~ s/^:\s*//) {
      @categories = split(/\s*:\s*/, $line);
    } else {
      # Must be a movie
      push_onto \%list, $line, @categories;
    }
  }
  close MLIST;

#  use Data::Dumper; $Data::Dumper::Indent = 1; print Dumper(\%list);
  %list;
}

sub dump_rows($;$);
sub dump_rows($;$) {
  my $href = shift;
  my $indent = shift || "";

  print  OUT "\n";
  print  OUT "$indent<ul>\n";

  foreach my $m (@{$href->{movies}}) {
    (my $link = "$m->{name} ($m->{year})")
      =~ s/([^a-zA-Z0-9])/sprintf("%%%02X",ord($1))/ge;

    my $name = $m->{name};
    $name =~ s/^([\"\'])?(.*), (A|The|La|Le|Die)\1/$1$3 $2$1/;
    $name =~ s/^(.*), (A|The|La|Le|Die)$/$2 $1/;

    printf OUT "  $indent<li><a class=\"%s\" href=\"%s%s\">%s</a></li>\n",
                       $m->{class}, $URL, $link, escapeHTML($name);
    $Total{lc $m->{class}}++;
    $Total{__all__}++;
  }

  if (exists $href->{sub}) {
    foreach my $cat (sort keys %{$href->{sub}}) {
      print  OUT "$indent<p>\n";
      printf OUT "$indent<li><b>%s</b></li>\n", $cat;
      dump_rows $href->{sub}->{$cat}, "  $indent";
    }
  }

  print OUT "$indent</ul>\n";
}







# END   helper subs
###############################################################################

############################## CODE BEGINS HERE ###############################

use File::stat;

# If output HTML file is newer than source file, exit without doing anything
stat($MLIST)->mtime   < stat($OUT)->mtime
  && stat($0)->mtime  < stat($OUT)->mtime
  && stat($IN)->mtime < stat($OUT)->mtime
  && !$force
  and exit 0;


my %list = read_movies;


#
# Now start the HTML
#
my $tmp = sprintf("%s.tmp.%05d", $OUT, $$);
open OUT, '>', $tmp
  or die "$ME: open( >$tmp ): $!\n";

#
# Read in the source HTML template
#
open IN, '<', $IN
  or die "$ME: open( $IN ): $!\n";
while (<IN>) {
    last if /WE INTERRUPT THIS PROGRAM FOR THIS SPECIAL ANNOUNCEMENT/;
    print OUT $_;
}

# ........
foreach my $cat (sort keys %list) {
  print  OUT "<hr>\n";
  print  OUT "<h1>$cat</h1>\n";
  print  OUT "<p>\n";
  dump_rows $list{$cat};
}

# Display total
print OUT "<hr>\n";
print OUT "<b>Total:</b>\n";
foreach my $class (sort keys %Total) {
  $class =~ /^_/ and next;
  printf OUT " <span class=\"%s\">%d %s</span>,\n",
    $class, $Total{$class},uc $class;
}
printf OUT " %d in all\n", $Total{__all__};

#
# Finish reading and writing out the template.
#
while (<IN>) {
  if (m!^(Last modified:)\s+!i) {
    my ($d,$m,$y) = (localtime(stat($MLIST)->mtime))[3,4,5];
    printf OUT "%s %d %s %d\n", $1, $d, $MoY[$m], $y + 1900;
    next;
  }
  print OUT;
}

close IN;
close OUT;
chmod 0444, $tmp;

unless ($dry_run) {
  my $bak = "$OUT~";
  unlink                 $bak;
  link           $OUT => $bak		if -f $OUT;
  rename $tmp => $OUT
    or die "$ME: Error renaming $tmp: $!\n";
}
