#!/usr/bin/perl -w
use strict;
use Image::Magick;
use File::Basename;

#
# htmlthumbnail version 2.0
#
# Generate thumbnails from a directory tree full of images
# http://www.phred.org/~alex/htmlthumbnail.pl
# 
# Basic algorithm
#   * Look for image files in a directory
#   * If we find a subdirectory recurse into there
#   * If we find an image that is over 800 pixels in it's longest length
#     than generate reduced/$file
#   * Create a thumbnail
#   * Montage the first three images into foldericon.png
#   * Write index.html with the results
#
# Requirements:
#   * ImageMagick and PerlMagick
#   * exiftags (http://johnst.org/sw/exiftags/)
# 
# Bugs/Issues:
#   * It will stomp index.html files that it didn't write
#   * We should automatically remove thumbnails and reduced images when the
#     source image is deleted
#
# This code is Copyright 2003 Alex Wetmore. 
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met: 
#
# 1.  Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer. 
#
# 2.  Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided with
# the distribution. 
#
# 3.  All advertising materials mentioning features or use of this
# software must display the following acknowledgement:  This product
# includes software developed by Alex Wetmore. 
#
# 4.  The name of Alex Wetmore may be used to endorse or promote
# products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# Originally based on ideas from HTMLThumbnail:
# 'HTMLThumbnail', written by Benjamin Franz, snowhare@nihongo.org
# version 1.1.3b.  I started by hacking up his script, but ended up
# just doing a complete rewrite to get the features that I wanted.
#

#

# thumbnail size and reduced picture size
my $default_thumbnailsize = 100; 
my $default_reducedsize = 800; 
my $default_highlightsize = 320; 

# Border size on images
my $default_border = 0;

# JPEG quality setting for thumbnail and reduced picture
my $default_qualityfactor = 70; 
my $default_rqualityfactor = 92;

# Rule of thumb: $imagesperrow=int(400/$thumbnailsize)
my $default_imagesperrow = 4;  

# name of the sub-directory to place the icon images in
my $default_thumbnaildirectory = "thumbnail";
my $default_reduceddirectory = "reduced";

# Where to write the results
my $default_outputfile = "index.html";

# boolean options
my $default_exif = 1;
my $default_jhead = 0;
my $default_recurse = 1;
my $default_forcerebuild = 0;
my $default_anongroup = 1;

# colors for the <body> tag
my $bgcolor = "#00005d";
my $textcolor = "#f0f0d0";
my $linkcolor = "#d5a632";
my $alinkcolor = "#e5d652";
my $vlinkcolor = "#b5a642";

sub Usage {
	print << "EOF";
htmlthumbnail 2.0 - by alex wetmore (alex\@phred.org)

usage: htmlthumbnail [options]

options:
  -border [width] - border with for tables
  -imagesperrow [count] - number of images to put on a row
  -thumbnailsize [pixels] - maximum dimension in pixels for a thumbnail
  -reducedsize [pixels] - maximum dimension in pixels for a reduced image
  -highlightsize [pixels] - maximum dimension in pixels for a highlight image
  -forcerebuild - rebuild all thumbnails, don't used existing ones
  -exif - read exif information for JPEGs
  -noexif - don't read exif information for JPEGs
  -recurse - recurse into subdirectories
  -norecurse - don't recurse into subdirectories
  -anongroup - create an anonymous group for pictures not in other groups
  -noanongroup - don't create an anonymous group
  -help - show usage information
  -morehelp - describe the htmlthumbnail.in file
  -example - show a sample htmlthumbnail.in
EOF
}

sub Usage2 {
	print << "EOF";
htmlthumbnail 2.0 - by alex wetmore (alex\@phred.org)

config file:
  Htmlthumbnail reads a template from htmlthumbnail.in in each 
  directory that it looks at.  The template file consists of 
  verbs that start with \$ and then text for the verbs.  

verbs:
  \$TITLE - The title for the page
  \$OPTIONS - Command line options
  \$HEADER - A HTML header that goes above all images and groups.
  \$FOOTER - A HTML footer that goes below all images and groups.
  \$IGNORE-FILES - Ignore these files.  Wildcards are okay.  There should
    be one file specification per line.
  \$GROUP-NEW - Create a new group.  The text is the group title.  All
    group commands apply to this group until a new group is created.
  \$GROUP-HEADER - Header text for the group.  If there is a highlight
    this text goes next to it.
  \$GROUP-FOOTER - Footer text for the group
  \$GROUP-FILES - Picture files to place in this group.  Wildcards are okay.
    There should be one file specification per line.
  \$GROUP-HIGHLIGHT - A single file to highlight for this group.
EOF
}

sub Example {
	print << "EOF";
\$TITLE
example page
\$OPTIONS
-noexif
-norecurse
\$HEADER
<p>A sample htmlthumbnail page.</p>
\$GROUP-NEW
first group, jpegs and pngs
\$GROUP-FILES
*.jpg
*.png
\$GROUP-NEW
second group, gifs
\$GROUP-FILES
*.gif
\$GROUP-NEW
third group, subdirectory
\$GROUP-FILES
morepictures/*
EOF
}

# return a configuration template with the default global settings
sub GetDefaults {
	my $template = {};

	$template->{BORDER} = $default_border;
	$template->{THUMBNAILSIZE} = $default_thumbnailsize;
	$template->{REDUCEDSIZE} = $default_reducedsize;
	$template->{HIGHLIGHTSIZE} = $default_highlightsize;
	$template->{QUALITYFACTOR} = $default_qualityfactor;
	$template->{RQUALITYFACTOR} = $default_rqualityfactor;
	$template->{IMAGESPERROW} = $default_imagesperrow;
	$template->{THUMBNAILDIR} = $default_thumbnaildirectory;
	$template->{REDUCEDDIR} = $default_reduceddirectory;
	$template->{OUTPUTFILE} = $default_outputfile;
	$template->{EXIF} = $default_exif;
	$template->{EXIFTITLEONLY} = 0;
	$template->{JHEAD} = $default_jhead;
	$template->{RECURSE} = $default_recurse;
	$template->{FORCEREBUILD} = $default_forcerebuild;
	$template->{ANONGROUP} = $default_anongroup;

	return $template;
}

# load the options from the command line or an options string 
# into a template.  use defaults from a parent template.
# usage: ProcessOptions(parent_template, options...)
sub ProcessOptions {
	my $template = shift;
	my $parent_template = shift;

	$$template->{BORDER} = $$parent_template->{BORDER};
	$$template->{THUMBNAILSIZE} = $$parent_template->{THUMBNAILSIZE};
	$$template->{REDUCEDSIZE} = $$parent_template->{REDUCEDSIZE};
	$$template->{HIGHLIGHTSIZE} = $$parent_template->{HIGHLIGHTSIZE};
	$$template->{QUALITYFACTOR} = $$parent_template->{QUALITYFACTOR};
	$$template->{RQUALITYFACTOR} = $$parent_template->{RQUALITYFACTOR};
	$$template->{IMAGESPERROW} = $$parent_template->{IMAGESPERROW};
	$$template->{THUMBNAILDIR} = $$parent_template->{THUMBNAILDIR};
	$$template->{REDUCEDDIR} = $$parent_template->{REDUCEDDIR};
	$$template->{OUTPUTFILE} = $$parent_template->{OUTPUTFILE};
	$$template->{EXIF} = $$parent_template->{EXIF};
	$$template->{EXIFTITLEONLY} = $$parent_template->{EXIFTITLEONLY};
	$$template->{JHEAD} = $$parent_template->{JHEAD};
	$$template->{RECURSE} = $$parent_template->{RECURSE};
	$$template->{FORCEREBUILD} = $$parent_template->{FORCEREBUILD};
	$$template->{ANONGROUP} = $$parent_template->{ANONGROUP};

	while (my $arg = shift) {
		if ($arg =~ /-border/i)
		{
			$$template->{BORDER} = shift;
		} elsif ($arg =~ /-imagesperrow/i) {
			$$template->{IMAGESPERROW} = shift;
		} elsif ($arg =~ /-thumbnailsize/i) {
			$$template->{THUMBNAILSIZE} = shift;
		} elsif ($arg =~ /-reducedsize/i) {
			$$template->{REDUCEDSIZE} = shift;
		} elsif ($arg =~ /-highlightsize/i) {
			$$template->{HIGHLIGHTSIZE} = shift;
		} elsif ($arg =~ /-noexif/i) {
			$$template->{EXIF} = 0;
		} elsif ($arg =~ /-exif/i) {
			$$template->{EXIF} = 1;
		} elsif ($arg =~ /-exiftitleonly/i) {
			print "titleonly\n";
			$$template->{EXIFTITLEONLY} = 1;
		} elsif ($arg =~ /-nojhead/i) {
			$$template->{JHEAD} = 0;
		} elsif ($arg =~ /-jhead/i) {
			$$template->{JHEAD} = 1;
		} elsif ($arg =~ /-f|-forcerebuild/i) {
			$$template->{FORCEREBUILD} = 1;
		} elsif ($arg =~ /-recurse/i) {
			$$template->{RECURSE} = 1;
		} elsif ($arg =~ /-(no|dont)recurse/i) {
			$$template->{RECURSE} = 0;
		} elsif ($arg =~ /-anongroup/i) {
			$$template->{ANONGROUP} = 1;
		} elsif ($arg =~ /-noanongroup/i) {
			$$template->{ANONGROUP} = 0;
		} elsif ($arg =~ /-help/i) {
			Usage();
			exit;
		} elsif ($arg =~ /-morehelp/i) {
			Usage2();
			exit;
		} elsif ($arg =~ /-example/i) {
			Example();
			exit;
		}
	}

	# build up the list of files that we always ignore
	push @ { $$template->{IGNOREFILES} }, "$$template->{REDUCEDDIR}*";
	push @ { $$template->{IGNOREFILES} }, "$$template->{THUMBNAILDIR}*";
	push @ { $$template->{IGNOREFILES} }, "foldericon.png";
	push @ { $$template->{IGNOREFILES} }, ".*";
	push @ { $$template->{IGNOREFILES} }, "*.html";
	push @ { $$template->{IGNOREFILES} }, "*.txt";
	push @ { $$template->{IGNOREFILES} }, "*.bak";
	push @ { $$template->{IGNOREFILES} }, "htmlthumbnail*";
	push @ { $$template->{IGNOREFILES} }, "*.db";
}

# states used by ProcessTemplate
my $State_Command = 0;
my $State_Text = 1;
my $State_TextJoin = 2;
my $State_Array = 3;

# build a new group structure
sub NewGroup {
	my $group = {};
	$group->{TITLE} = "";
	$group->{FILEGLOBS} = [];
	$group->{HEADER} = "";
	$group->{FOOTER} = "";
	$group->{ANON} = 0;
	$group->{HIGHLIGHT} = 0;
	$group->{HIGHLIGHTFILE} = [];
	return $group;
}

# process a template file and put the results into a template data
# structure
sub ProcessTemplate {
	my $template;
	my @groups;

	$template = {};
	$template->{TITLE} = shift;
	$template->{HEADER} = "";
	$template->{FOOTER} = "";
	$template->{OPTIONS} = "";
	$template->{GROUPS} = [];
	$template->{IGNOREFILES} = [];

	if (open(T,"<htmlthumbnail.in")) {
        my $state = $State_Command;
        my $refCurrent;
		my $groupCurrent;

		while (<T>)
		{
			my $line = $_;
		    my $command = "";
			if ($line =~ /^#/) {
				next;
			}
            if ($line =~ /^\$(.*)$/) {
                $state = $State_Command;
                $command = $1;
            }

            if ($state == $State_Command)
            {
                if ($command eq "OPTIONS")
                {
                    $state = $State_TextJoin;
                    $refCurrent = \$template->{OPTIONS};
					$$refCurrent = "";
                } elsif ($command eq "TITLE") {
                    $state = $State_TextJoin;
                    $refCurrent = \$template->{TITLE};
					$$refCurrent = "";
                } elsif ($command eq "HEADER") {
                    $state = $State_Text;
                    $refCurrent = \$template->{HEADER};
                } elsif ($command eq "FOOTER") {
                    $state = $State_Text;
                    $refCurrent = \$template->{FOOTER};
				} elsif ($command eq "IGNORE-FILES") {
					$refCurrent = \$groupCurrent->{IGNOREFILES};
					$state = $State_Array;
                } elsif ($command eq "GROUP-NEW") {
					$groupCurrent = NewGroup();
					push @groups, $groupCurrent;
					$refCurrent = \$groupCurrent->{TITLE};
					$state = $State_TextJoin;
				} elsif ($command eq "GROUP-FILES") {
					$refCurrent = \$groupCurrent->{FILEGLOBS};
					$state = $State_Array;
				} elsif ($command eq "GROUP-HEADER") {
					$refCurrent = \$groupCurrent->{HEADER};
					$state = $State_Text;
				} elsif ($command eq "GROUP-HIGHLIGHT") {
					$groupCurrent->{HIGHLIGHT} = 1;
					$refCurrent = \$groupCurrent->{HIGHLIGHTFILE};
					$state = $State_Array;
				} elsif ($command eq "GROUP-FOOTER") {
					$refCurrent = \$groupCurrent->{FOOTER};
					$state = $State_Text;
				} else {
                    print "Unknown command $command in htmlthumbnail.in\n";
                    exit;
                }
            }
            elsif ($state == $State_Text)
            {
                $$refCurrent .= $line;
            }
            elsif ($state == $State_TextJoin)
            {
                chomp $line;
                $$refCurrent .= "$line ";
            }
			elsif ($state == $State_Array)
			{
				chomp $line;
				push @{ $$refCurrent }, $line;
			}
		}
		close T;
	} else {
		print "No template here\n";
	}

	my $groupAnonymous = {};
	$groupAnonymous->{TITLE} = "";
	my @fileglobs = "*";
	$groupAnonymous->{FILEGLOBS} = \@fileglobs;
	$groupAnonymous->{HEADER} = "";
	$groupAnonymous->{FOOTER} = "";
	$groupAnonymous->{ANON} = 1;
	push @groups, $groupAnonymous;
	$template->{GROUPS} = \@groups;

	print "found groups:\n";
	foreach my $group (@groups) {
		print "  $group->{TITLE}\n";
	}

	return $template;
}

sub min {
	my $left = shift;
	my $right = shift;
	if ($left < $right) { return $left; } else { return $right; }
}

# create a new picture object
sub NewPicture {
	my $picture = {};
	$picture->{FILE} = "";
	$picture->{REDUCED} = 0;
	$picture->{THUMBNAILFILE} = "";
	$picture->{REDUCEDFILE} = "";
	$picture->{EXIF} = 0;
	$picture->{EXIFDATE} = "";
	$picture->{EXIFTITLE} = "";
	$picture->{EXIFEXPOSURE} = "";
	$picture->{FILETITLE} = "";
	$picture->{WIDTH} = "";
	$picture->{HEIGHT} = "";
	$picture->{RWIDTH} = "";
	$picture->{RHEIGHT} = "";
	$picture->{TWIDTH} = "";
	$picture->{THEIGHT} = "";
	$picture->{DIR} = 0;
	$picture->{DIRHTML} = "";
	return $picture;
}

# 
# process pictures from a fileglob and return an array with information 
# for them
#
# arguments:
#  $fileglob - files to look at
#  $hFiles - reference to the used file hash
#  $template - reference to the template for this level
# returns:
#  array of picture hashrefs
#
sub GetPictures {
	my @globfiles = glob shift;
	my $hFiles = shift;
	my $template = shift;
	my $fHighlight = shift;

	my @files;
	my @pictures;

	# parse the template parameters that we care about
	my $thumbnailsize = $$template->{THUMBNAILSIZE}; 
	my $qualityfactor = $$template->{QUALITYFACTOR}; 
	my $rqualityfactor = $$template->{RQUALITYFACTOR};
	my $thumbnaildirectory = $$template->{THUMBNAILDIR};
	my $reducedsize = $$template->{REDUCEDSIZE}; 
	my $reduceddirectory = $$template->{REDUCEDDIR};
	if ($fHighlight)
	{
		$thumbnailsize = $$template->{HIGHLIGHTSIZE}; 
		$thumbnaildirectory = $$template->{HIGHLIGHTDIR};
	}

	# build the list of files that we'll look at
	foreach my $f (@globfiles) 
	{
		# ignore files on our ignore list
		if (exists $$hFiles{$f}) { next; }

		# ignore directories if we shouldn't be recursing
		if ($$template->{RECURSE} == 0 && (-d $f)) { next; }

		# if we got through those checks then add the file to
		# the list of files to show
		push @files, $f;

		# keep track of displayed files
		$$hFiles{$f} = 1;
	}

	my $pwd = `pwd`;


	# look at each file
	foreach my $file (@files)
	{
		my $picture = NewPicture();
		my $basename = basename($file);
		my $dirname = dirname($file);
		my $reduceddirectory = "$dirname/$$template->{REDUCEDDIR}";
		my $thumbnaildirectory = "$dirname/$$template->{THUMBNAILDIR}";

		# append the reduced size to the directory name if it isn't
		# the standard 800 pixels
		if ($reducedsize != $default_reducedsize) {
			$reduceddirectory .= "-$reducedsize";
		}
		if ($thumbnailsize != $default_thumbnailsize) {
			$thumbnaildirectory .= "-$thumbnailsize";
		}

		# make the thumbnail directory if it doesn't already exist
		if (! -e $thumbnaildirectory) {
			mkdir ($thumbnaildirectory,0755) ||
				die ("No $thumbnaildirectory directory and could not create\n$!");
			chmod (0755,$thumbnaildirectory);
		}

		if (! -d $thumbnaildirectory) {
			die ("$thumbnaildirectory exists, but is not a directory\n");
		}

		# make the reduced directory if it doesn't already exist
		if (! -e $reduceddirectory) {
			mkdir ($reduceddirectory,0755) ||
				die ("No $reduceddirectory directory and could not create\n$!");
			chmod (0755,$reduceddirectory);
		}

		if (! -d $reduceddirectory) {
			die ("$reduceddirectory exists, but is not a directory\n");
		}

		if ((-d $file)) {
			# we found a directory.  recurse into it. 
			chdir $file;
			$picture->{DIR} = 1;
			$picture->{FILE} = $file;
			$picture->{DIRHTML} .= ProcessDir($template, $file);
			chdir "..";
			print STDERR "*** back to processing $pwd\n";
		} else {
			if (($file =~ /.jpg$/i) && ($$template->{EXIF} == 1)) {
				# get the EXIF tags from the image
				open EXIF, "exiftags -a -u \"$file\" 2>&1 |";
				my $imageFStop = "";
				my $imageShutter = "";
				my $imageISO = "";
				while (<EXIF>) {
					chomp;
					if ($_ =~ /^([\w\s-]+):\s*(.*)\s*$/) {
						my $tag = $1;
						my $value = $2;
						if  	($tag eq "Image Created" && $$template->{EXIFTITLEONLY} == 0) {
							$picture->{EXIFDATE} = $value;
							$picture->{EXIF} = 1;
						} elsif ($tag eq "F-Number" && $$template->{EXIFTITLEONLY} == 0) {
							$imageFStop = $value;	
							$picture->{EXIF} = 1;
						} elsif ($tag eq "Exposure Time" && $$template->{EXIFTITLEONLY} == 0) {
							$imageShutter = $value;
							if ($imageShutter =~ /10.(\d+)\s*sec/)
							{
								$imageShutter = "1/" . $1 / 10;
							}
							$picture->{EXIF} = 1;
						} elsif ($tag eq "ISO Speed Rating") {
							$imageISO = "iso" . $value;
						} elsif ($tag eq "Title" && !($value =~ /DIGITAL CAMERA/)) {
							$picture->{EXIFTITLE} = "$value";
							$picture->{EXIF} = 1;
						}
					}
				}
				close EXIF;

				if ($picture->{EXIFTITLE} eq "" && $$template->{JHEAD})
				{
					# get the EXIF tags from the image
					open EXIF, "jhead \"$file\" 2>&1 |";
					while (<EXIF>) {
						chomp;
						if ($_ =~ /^(\w[\w\s-]+\w)\s*:\s*(.*)\s*$/) {
							my $tag = $1;
							my $value = $2;
							if  	($tag eq "Comment") {
								$picture->{EXIFTITLE} = "$value";
								$picture->{EXIF} = 1;
							}
						}
					}
					close EXIF;
				}

				if ($imageFStop ne "" && $imageShutter ne "")
				{
					$picture->{EXIFEXPOSURE} = "$imageFStop - $imageShutter";
				}

				if ($imageISO ne "")
				{
					$picture->{EXIFEXPOSURE} = 
						"$picture->{EXIFEXPOSURE} - $imageISO";
				}
			}

			# create the image object.  we don't actually load anything
			# in it until we are sure that we need to.
			my $image = Image::Magick->new;
			my $bReadFile = 0;
			my $width;
			my $height;

			# get the width and height in pixels for our image
			($width, $height) = $image->Ping($file) or next;

			$picture->{FILE} = $file;
			$picture->{FILETITLE} = $basename;
			$picture->{WIDTH} = $width;
			$picture->{HEIGHT} = $height;

			# Create the reduced file
			if ($width > $reducedsize || $height > $reducedsize) {
				my $rfile = "$reduceddirectory/$basename";
				my $rwidth;
				my $rheight;

				if ($$template->{FORCEREBUILD} || ! (-f "$rfile")) {
					# reduced file doesn't exist, but it should, so make it
					print STDERR "generating $rfile\n";
					if (!$bReadFile) {
						$image->Read(filename=>$file);
						$bReadFile = 1;
					}
					$image->UnsharpMask(radius=>0.7, amount=>2.0, threshold=>0);
					$image->Scale(geometry=>"${reducedsize}x${reducedsize}");
					($rheight, $rwidth) = $image->Get("height", "width");
					$image->Set(quality=>$rqualityfactor);
					$image->Write(filename=>"$rfile");
				} else {
					print STDERR "using existing $rfile\n";
					($rwidth,$rheight) = $image->Ping("$rfile");
				}

				$picture->{REDUCEDFILE} = $rfile;
				$picture->{REDUCED} = 1;
				$picture->{RWIDTH} = $rwidth;
				$picture->{RHEIGHT} = $rheight;
			}

			# Create the thumbnail
			my $tfile = "$thumbnaildirectory/$basename";
			my $twidth;
			my $theight;
			if ($$template->{FORCEREBUILD} || ! (-f "$tfile")) {
				# thumbnail file doesn't exist, but it should, so make it
				print STDERR "generating $tfile\n";
				if (!$bReadFile) {
					$image->Read(filename=>$file);
					$bReadFile = 1;
				}
				$image->Scale(geometry=>"${thumbnailsize}x${thumbnailsize}");
				($theight, $twidth) = $image->Get("height", "width");
				$image->Set(quality=>$qualityfactor);
				$image->Write(filename=>"$tfile");
				$picture->{THUMBNAIL} = $image;
			} else {
				print STDERR "using existing $tfile\n";
				($twidth,$theight) = $image->Ping("$tfile");
				$image->Read(filename=>$tfile);
				$picture->{THUMBNAIL} = $image;
			}

			$picture->{THUMBNAILFILE} = $tfile;
			$picture->{TWIDTH} = $twidth;
			$picture->{THEIGHT} = $theight;

		}

		push @pictures, $picture;
	}

	return @pictures;
}

sub DrawPicture {
	my $picture = shift;
	my $fHighlight = shift;
	my $border = shift;

	my $html = "";
	my $alt = "";
	my $desc = "";

	# sanatize the file names
	$$picture->{FILE} =~ s/ /%20/g;
	$$picture->{THUMBNAILFILE} =~ s/ /%20/g;
	$$picture->{REDUCEDFILE} =~ s/ /%20/g;


	if ($$picture->{EXIF}) {
		if ($$picture->{EXIFTITLE} ne "") {
			$desc .= "<br>$$picture->{EXIFTITLE}<br>";
			#$desc .= "<small>$$picture->{FILETITLE}<br>";
			$alt = $$picture->{EXIFTITLE};
		} else {
			#$desc .= "<br>$$picture->{FILETITLE}<br><small>";
			$alt = $$picture->{FILETITLE};
		}
		$desc .= "<small>";
		if ($$picture->{EXIFDATE}) {
			$desc .= "$$picture->{EXIFDATE}<br>";
		}
		if ($$picture->{EXIFEXPOSURE}) {
			$desc .= "$$picture->{EXIFEXPOSURE}<br>";
		}
		$desc .= "</small>";
	} else {
		#$desc .= "<br>$$picture->{FILETITLE}<br>\n";
		$alt = $$picture->{FILETITLE};
	}

	# display information for a picture
	$html .= " <td>\n";
	if ($$picture->{REDUCED}) {
		$html .= "  <small><a href=\"$$picture->{REDUCEDFILE}\">";
	} else {
		$html .= "  <small><a href=\"$$picture->{FILE}\">";
	}
	$html .= "<img src=\"$$picture->{THUMBNAILFILE}\" ";
	$html .= "alt=\"$alt\" ";
	$html .= "height=$$picture->{THEIGHT} width=$$picture->{TWIDTH} ";
	$html .= "border=$border> ";
	if ($$picture->{REDUCED}) {
		$html .= "<br>$$picture->{RWIDTH} x $$picture->{RHEIGHT}</a>";
		$html .= " / <a href=\"$$picture->{FILE}\">";
	}
	$html .= "<br>$$picture->{WIDTH} x $$picture->{HEIGHT}</a></small>";
	if (!$fHighlight) { $html .= "<br>"; }

	$html .= $desc;

	$html .= " </td>\n";

	return $html;
}

#
# process all of the pictures in a subdirectory
#
# arguments: 
#   $dirname - name of the directory (relative to parent).  can be anything
#              for root folder
# returns a line of HTML with a link to this directory
#
sub ProcessDir {
	my $parent_template = shift;
	my $dirname = shift;

	my $pwd = `pwd`;
	chop $pwd;
	my $title = $dirname;
	$title =~ s/^.*\///;
	$title =~ s/^\./ /g;
	$title =~ s/-/ /g;

	print STDERR "*** $title -> $pwd\n";

    my $template = ProcessTemplate($title);
	ProcessOptions(\$template, $parent_template, split /\s+/, $template->{OPTIONS});

	my $file;					# filename
	my $image;					# PerlMagick image object for current image
	my $foldericon;				# PerlMagick image object for folder icon
	my $height;					# height of image
	my $width;					# width of image
	my $iheight;				# height of thumbnail
	my $iwidth;					# width of thumbnail
	my $rheight;				# height of reduced image
	my $rwidth;					# width of reduced image
	my $bodybgcolor = "";		# bgcolor tag in <body>
	my $bodytextcolor = "";		# textcolor tag in <body>
	my $bodylinkcolor = "";		# linkcolor tag in <body>
	my $bodyalinkcolor = "";	# alinkcolor tag in <body>
	my $bodyvlinkcolor = "";	# vlinkcolor tag in <body>
	my $bUpdateIcon = 1;		# do we need to update the icon for this folder
	my %hFiles;
	my $fFirstGroup = 1;
	my $outputfile = $template->{OUTPUTFILE};
	my $highlightsize = $template->{HIGHLIGHTSIZE};

	my $parenthtml = "  <td><a href=\"$dirname\"><img src=\"$dirname/foldericon.png\" border=$template->{BORDER} alt=\"[$title]\"><br><small>&lt;folder&gt;</small></a><br>$title</td>\n";
	my $html = "";

    # expand the options
	$title = $template->{TITLE};

	# setup the body tags
	$bodybgcolor    = "bgcolor=\"$bgcolor\"" if ($bgcolor);
	$bodytextcolor  = "text=\"$textcolor\"" if ($textcolor);
	$bodylinkcolor  = "link=\"$linkcolor\"" if ($linkcolor);
	$bodyalinkcolor = "alink=\"$alinkcolor\"" if ($alinkcolor);
	$bodyvlinkcolor = "vlink=\"$vlinkcolor\"" if ($vlinkcolor);

	# don't bother to update the icon for this folder if one already
	# exists
	if (-e "foldericon.png") {
		$bUpdateIcon = 0;
	}

	# setup the top of the HTML for this directory
	$html .= "<html>\n<head>\n<title>$title</title>\n</head>";
	$html .= "<body $bodybgcolor $bodytextcolor $bodylinkcolor $bodyvlinkcolor $bodyalinkcolor>\n";
	$html .= "<center>\n<h1>$title</h1>\n";

	if ($template->{HEADER}) {
		$html .= "<table>";
		$html .= "<colgroup><col><col width=600><col></colgroup>";
		$html .= "<td></td><td>";
		$html .= $template->{HEADER};
		$html .= "</td><td></td>";
		$html .= "</table>";
	}

	if ($bUpdateIcon) {
		# create a folder icon that looks like a folder of some sort
		$foldericon = Image::Magick->new;
		my $thumbnailsize = $template->{THUMBNAILSIZE};
		$foldericon->Set(size=>"${thumbnailsize}x${thumbnailsize}",
						 monochrome=>"False",
						 type=>"TrueColor");
		$foldericon->ReadImage("xc:$bgcolor");
		# bugbug - change points to work with thumbnail size
		$foldericon->Draw(primitive=>"polygon",
						  points=>"0,10,10,10,15,0,35,0,40,10,100,10,100,100,0,100,0,10",
						  fill=>"goldenrod",
						  stroke=>"black",
						  method=>"floodfill");
	}

	# walk through each file in the directory
	opendir D, ".";

	# build up the list of files to ignore and put them into %hFiles
	foreach my $ignoreglob (@ { $template->{IGNOREFILES} }) {
		my @globfiles = glob $ignoreglob;
		foreach my $f (@globfiles) {
			#print "ignoring file $f\n";
			$hFiles{$f} = 1;
		}
	}


	# go through each of our groups
	foreach my $group (@ { $template->{GROUPS} } ) {
		# skip the anonymous group if we shouldn't use it
		if (($template->{ANONGROUP} == 0) && $group->{ANON}) { next; }

		my $imagesperrow = $template->{IMAGESPERROW};  

		print "group found: $group->{TITLE}\n";

		# put a divider between groups
		if ($fFirstGroup) { 
			$fFirstGroup = 0; 
		} else { 
			$html .= "<hr width=600>\n"; 
		}

		my @pictures;
		my @highlightpictures;

		# get all of the highlight pictures
		if ($group->{HIGHLIGHT}) {
			foreach my $fileglob (@ { $group->{HIGHLIGHTFILE} } ) {
				push @highlightpictures, GetPictures($fileglob, \%hFiles, \$template, 1);
			}
		}

		# get all of the pictures for each file glob
		foreach my $fileglob (@ { $group->{FILEGLOBS} } ) {
			push @pictures, GetPictures($fileglob, \%hFiles, \$template, 0);
		}
		 
		# if we have no files then don't do anything
		if ((scalar(@pictures) == 0) && (scalar(@highlightpictures) == 0)) { 
			next; 
		}

		#
		# build the html for the pictures
		#

		if ($group->{TITLE})
		{
			$html .= "<h2>$group->{TITLE}</h2>\n";
		}

		$html .= "<table>";
		$html .= "<colgroup><col><col width=600><col></colgroup>";
		$html .= "<td></td><td>";

		# display the group title
		# insert the header if one is specified.  We force it to be at
		# most 600 pixels wide to make it readable
		my $fMinigrid = 0;
		if (scalar(@highlightpictures) > 0 && scalar(@pictures) <= 4) {
			my $picture = $highlightpictures[0];
			$html .= "<table>";
			$html .= "<colgroup><col><col width=\"$highlightsize\">";
			if ($group->{HEADER}) { $html .= "<col width=\"$highlightsize\">"; }
			$html .= "<col></colgroup>";
			$html .= "<tr valign=\"top\" align=\"center\">\n";
			$html .= "<td></td>";
			$html .= DrawPicture(\$picture, 1, $template->{BORDER});
			if ($group->{HEADER}) { 
				$html .= "<td align=\"left\" valign=\"bottom\">$group->{HEADER}"; 
			}
			if (scalar(@pictures) <= 4 && scalar(@pictures) > 0) {
				$html .= "<br>";
				$imagesperrow = 2;
				$fMinigrid = 1;
			} else {
				$html .= "</td><td></td>";
				$html .= "</tr>";
				$html .= "</table>";
			}
		} elsif ($group->{HEADER}) {
			$html .= "<table>";
			$html .= "<colgroup><col><col width=600><col></colgroup>";
			$html .= "<tr>";
			$html .= "<td></td><td align=\"left\">";
			$html .= $group->{HEADER};
			$html .= "</td><td></td>";
			$html .= "</tr>";
			if (scalar(@highlightpictures) > 0)
			{
				my $picture = $highlightpictures[0];
				$html .= "<tr align=\"center\"><td></td>";
				$html .= DrawPicture(\$picture, 1, $template->{BORDER});
				$html .= "<td></td></tr>";
			}
			$html .= "</table>";
		}

		if (scalar(@pictures) > 0)
		{
			# compute the number of columns for our table
			$html .= "<table cellpadding=5>\n";
			my $columns = min(scalar(@pictures), $imagesperrow);
			my $colwidth = 100 / $columns;
			$html .= "<colgroup>";
			for (my $i = 0; $i < $columns; $i++) {
				$html .= "<col width=\"$colwidth%\">";
			}
			$html .= "</colgroup>\n";

			# display each file in our group
			my $counter=0;
			foreach my $picture (@pictures) {
				# start a new row if it is that time
				if (($counter%$imagesperrow)==0) {
					$html .= "<tr valign=bottom align=center>\n";
				}

				$counter++;

				# foldericon contains entries from the first three pictures
				if ($bUpdateIcon && $counter <= 3) {
					# bugbug - change minithumbnail to be relative to thumbnailsize
					my $image = $picture->{THUMBNAIL};
					my $tiny = $template->{THUMBNAILSIZE} / 2;
					$image->Scale(geometry=>"${tiny}x${tiny}");
					$image->Border(width=>1, height=>1, fill=>"black");
					$foldericon->Composite(compose=>"copy", 
											x=>($counter*8),
											y=>($counter*8)+10,
											image=>$image);
				}

				if ($picture->{DIR}) {
					# display information fora directory
					$html .= $picture->{DIRHTML};
				} else {
					$html .= DrawPicture(\$picture, 0, $template->{BORDER});
				}

				# end of row if we are there
				if (($counter % $imagesperrow)==0) {
					$html .= "</tr>\n";
				}
			}
			$html .= "</table>";
		}
		
		if ($fMinigrid) { 
			$html .= "</td><td></td>";
			$html .= "</tr>";
			$html .= "</table>";
		}

		# insert the footer if one is specified.  We force it to be at
		# most 600 pixels wide to make it readable
		if ($group->{FOOTER}) {
			$html .= "<table>";
			$html .= "<colgroup><col><col width=600><col></colgroup>";
			$html .= "<td></td><td>";
			$html .= $group->{FOOTER};
			$html .= "</td><td></td>";
			$html .= "</table>";
		}

		$html .= "</td><td></td>";
		$html .= "</table>";
	}
	closedir D;

	if ($bUpdateIcon) {
		print STDERR "writing folder icon\n";
		$foldericon->Write(filename=>"foldericon.png");
	}

	$html .= "</table>\n$template->{FOOTER}</center>\n</body>\n</html>";

	if (!open(OUTPUTFILE,">$outputfile")) {
		die ("Could not open $outputfile for writing\n$!");
	}
	print OUTPUTFILE $html;
	close OUTPUTFILE;

	return $parenthtml;
}

my $defaults = GetDefaults();
ProcessOptions(\$defaults, \$defaults, @ARGV);
ProcessDir(\$defaults, "");

