#!/usr/bin/perl
#
# xml2html.pl - parse XML recipes
#
# $Id: xml2html.pl,v 1.19 2019/01/27 22:13:40 esm Exp $
#
(our $ME = $0) =~ s|.*/||;
(our $VERSION = '$Revision: 1.19 $ ') =~ tr/[0-9].//cd;

use strict;
use warnings;

use CGI qw(:standard);
use SelectSaver;
use XML::Parser;

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

# 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;
my $format  = "html";
use Getopt::Long;
GetOptions( 'debug!'     => \$debug,
	    'dry-run|n!' => \$dry_run,
	    force        => \$force,
	    'format=s'   => \$format,
	    help         => sub { usage 0 },
	    version      => sub { print "$ME version $VERSION\n"; exit 0 },
) or usage(1);

@ARGV
  or usage(1);

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

###############################################################################
# BEGIN HTML Style

package Recipe::HTML;

use strict;
use CGI qw(:standard);

use File::Basename;
use File::stat;

#############
#  _navbar  #
#############
sub _navbar($) {
  my $name = shift;

  return <<"+";

<!-- Navbar -->
<table bgcolor="#FFFF66" width="100%" border="0" cellpadding="0" cellspacing="0">
 <tr>
  <td align="left">&nbsp;<small><a href="../">Ed Santiago</a>
  <img src="../images/arrow_navbar.gif" width=13 height=9 align="bottom" alt="-&gt;">
  <a href="./">Recipes</a>
  <img src="../images/arrow_navbar.gif" width=13 height=9 align="bottom" alt="-&gt;">
  <b>$name</b></small>
  </td>
 </tr>
</table>
+
}


my $curr_section;
my $in_ingredient;

sub StartDocument {
  $curr_section = "";
  $in_ingredient = 0;
}

sub StartTag {
  my $self = shift;
  my $element = shift;
  my %attr = %_;

  if ($element eq 'recipe') {
    my $name = $attr{name}
      or die "$ME: No recipe name\n";

    print start_html(-title  => "Ed's Recipes: $name",
		     -author => 'esm',
		     -style  => { src => 'recipes.css' }), "\n",
	  _navbar($name),"\n",
          p,"\n",
          "<h1>";		# Unfinished!  Maybe we'll have a picture...

    my $base = basename($self->{Base}, '.xml');

    # Look for images with all sorts of extensions, jpg first, then ...
    foreach my $ext (qw(jpg jpeg png gif)) {
      my $img = "$base.$ext";
      if (-f $img) {
	# Found a picture!  Now see if we have a thumbnail, and it has
	# The same mtime as the picture itself.
	my $thumb = "$base-t.$ext";
	if (!-f $thumb || stat($thumb)->mtime != stat($img)->mtime) {
	  # No thumbnail, or old one.  Make a new one, with a suitable
	  # geometry for viewing.
	  unlink $thumb;
	  system 'convert',
	         -geometry => "120x120",
		 $img => $thumb;
	  die "$ME: Error converting thumbnail for $img\n" if $?;

	  # Make the thumbnail have the same mtime and permissions as the pic
	  my $st = stat($img);
	  utime $st->mtime, $st->mtime, $thumb
	    or die "$ME: Error setting mtime for $thumb: $!\n";
	  chmod $st->mode & 03777 => $thumb
	    or die "$ME: Error setting file mode for $thumb: $!\n";
	}

	# Find out actual thumbnail dimensions.  We should probably use
	# the Image::Magick module for this, rather than (ick) exec'ing.
	my $ident = `identify $thumb`;
	my ($w, $h) = ($ident =~ /\s(\d+)x(\d+)\s/)
	  or die "Cannot identify $thumb: '$ident'\n";

	# Emit HTML for the picture.  Remember, we're in the <h1>
	# from up above, so this will be part of the page title.
	print  "<a href=\"$img\"><img class=\"thumb\" src=\"$thumb\" width=$w height=$h alt=\"Picture of $name\"></a>\n";
	last;
      }
    }

    # Recipe name, and be sure to close the <h1> tag.
    print  $name,"</h1>\n",
	   "<br clear=\"all\" />\n";

    print "<table cellpadding=5 width=\"100%\">\n";
    print " <tr>\n";
    foreach my $att (qw(source quantity time)) {
      printf " <td>%s: <b>", $att;
      printf "<a href=\"%s\">", $attr{url}  if $att eq 'source' && $attr{url};
      print  $attr{$att} || "&nbsp;";
      print  "</a>"			    if $att eq 'source' && $attr{url};
      print  "</b></td>\n";
    }
    print  " </tr>\n",
           "</table>\n";

  } elsif ($element eq 'i') {
    $in_ingredient = 1;
  } else {
    if ($element ne $curr_section) {
      if ($curr_section eq 'ingredient') {
	print "</table>\n";
      } elsif ($curr_section eq 'direction') {
	print "</ol>\n";
      }
      print hr,"\n";
      unless ($element =~ /^(blurb|prefix)$/) {
	(my $section = ucfirst $element) =~ s/([^s])$/$1s/;
	print h2($section),"\n";
      }
      print p,"\n";

      if ($element eq 'ingredient') {
	print "<table>\n";
      } elsif ($element eq 'direction') {
	print "<ol>\n";
      }
      $curr_section = $element;
    }

    if ($element eq 'ingredient') {
      print  " <tr>\n";
      printf "  <td align=\"right\">%s</td>\n", $attr{amount} || "&nbsp;";
      printf "  <td>%s</td>\n", $attr{units} || "&nbsp;";
    } elsif ($element eq 'direction') {
      print " <li>";
    }
  }
}
sub EndTag {
  my $self = shift;
  my $element = shift;

  if ($element eq 'ingredient') {
    print " </tr>\n";
  } elsif ($element eq 'direction') {
    print "</li>\n";
  } elsif ($element eq 'i') {
    $in_ingredient = 0;
  }
}
sub Text {
  if ($curr_section eq 'ingredient') {
    $_ = "<i>$_</i>";			# Highlight ingredient name...
    s!(,\s+.*)</i>$!</i>$1!;		# ...but not "butter, softened"
    s!(\s+\(.*\))</i>$!</i>$1!;		# ...or "eggs (separated)"

    print  "  <td>$_</td>\n";
  } elsif (/^\s*$/) {
    print p,"\n";
  } else {
    print "<i>"  if $in_ingredient;
    print $_;
    print "</i>" if $in_ingredient;
    print "\n";
  }
}
sub EndDocument {
  my $self = shift;

  my $src = $self->{Base};
  my $st = stat($src);
  (my $mtime = scalar localtime($st->mtime)) =~ s/\s+\d+:\d+:\d+\s+/, /;

  print "</ol>\n",
        hr,"\n",
        "Source: <samp><a href=\"$src\">$src</a></samp>,\n",
        " last updated $mtime\n",
        p,"\n",
        "<address><a href=\"../\">Ed Santiago</a>&nbsp;&lt;<a href=\"mailto:ed\@edsantiago.com\">ed\@edsantiago.com</a>&gt;</address>\n",
        end_html,"\n";
}

package main;

# END   HTML Style
###############################################################################

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

my $p = XML::Parser->new( Style => "Stream", Pkg => "Recipe::HTML" );

foreach my $f (@ARGV) {
  (my $out = $f) =~ s/\.xml$/\.$format/
    or die "$ME: Unable to determine output filename for '$f'\n";
  my $tmp = $out . ".TMP.$$";
  unlink $tmp;

  open OUT, '>', $tmp
    or die "$ME: Error creating $tmp: $!\n";

  my $saver = SelectSaver->new(*OUT);

  # Do it!
  $p->parsefile($f);

  # Move into place
  chmod 0444 => $tmp;
  unlink                 "$out.BAK";
  rename         $out => "$out.BAK";
  rename $tmp => $out
    or die "$ME: Error moving $tmp to $out: $!\n";
}
