#!/usr/bin/perl -w
# jGal - Jason's Gallery
# 
####################
#  GNU General Public License
#
#  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
#
####################
# Author: Jason Paul
#
# Modification History
# --------------------
#* Date    : 8/7/2002
#  Version : 0.1
#  - Project started
#* Date    : 10/20/2002
#  Version : v1.0rc1
#  - The first release canidate. Heavy testing begins.
#* Date    : 11/01/2002
#  Version : v1.0rc2
#  - Second release canidate.
#  - Removed dependency on ImageMagickVersion in the getImgInfo function.
#  - Fixed some slide templates to work correctly when info went off the
#    edge of the screen.
#  - Removed dependency on the File::Copy function by using convert instead
#    of mogrify
#
################################################################################

use Getopt::Long; # options parsing
use Cwd;          # directory functions

######################
### setup some variables
######################

my $version     = "v1.0rc2";
my $author      = "Jason Paul";
my $progName    = $0;
my $jpgTypeStr = "*.[jJ][pP][gG] *.[jJ][pP][eE][gG]";
my $gifTypeStr = "*.[gG][iI][fF]"; 
my $fileTypeStr = "$jpgTypeStr $gifTypeStr"; 
my $jGalRCFile  = "$ENV{HOME}/.jGalrc"; # name of users rc file
my $galDatFile  = "gallery.dat";    # name of gallery data file
my $thumbDir    = "thumbs";         # thumbnail directory
my $thumbPrefix = "$thumbDir/thumb_"; # thumbnail prefix
my $slideDir    = "slides";         # slide directory
my $slidePrefix = "$slideDir/slide_"; # slide prefix
my $exifProg    = "jhead";          # program to extract exif info
my $imgInfoProg = "identify";       # program to display image info
my $scaleProg = "convert";        # program to resize images
my $indexFile   = "index.html";     # name of index file
my $indexTmpl   = "index_template"; # name of index template file
my $slideTmpl   = "slide_template"; # name of slide template file
my $infoTmpl    = "info_template";  # name of info template file
my @imgMgkVer   = &getImgMgkVer;    # ImageMagick version

######################
### end variable setup
######################

# Get the list of directories we're going to be running the
# program on.
my @dirs = &getDirs;

# Initial system-wide options to the defaults
# This needs to be done first.
my %optsSys = &setSystemOpts;

# check to see if the user has a resource file in their home dir.
# If they do get these options here.
my %optsRC = &getRCOpts;
# check the validity of the options (i.e. bounds check)
%optsRC = &checkOpts(\%optsRC, \%optsSys);

# get the command line options. 
print "Checking the command line options.\n";
my %optsCmd = &getCmdOpts({});
# check the validity of the options (i.e. bounds check)
%optsCmd = &checkOpts(\%optsCmd, \%optsSys);
    
# save the starting dir so we know how to get back
my $startDir = cwd;

# run on each directory given on the cmdLine.
foreach $dir (@dirs) {

    # skip any directory that is not valid
    if (! (-d $dir)) {
        for (my $i=0;$i<30;$i++) {print "-"};
        print "\n\'$dir\' is not a valid directory. Skipping!\n";
        next;
    }

    die "Can't cd to $dir: $!\n" unless chdir $dir;
    for (my $i=0;$i<30;$i++) {print "-"};
    print "\nProcessing directory \'$dir\'\n\n";

    # get any options from this dirs gallery.dat file
    my %optsGal = &getGalOpts;
    # check the validity of the options (i.e. bounds check)
    %optsGal = &checkOpts(\%optsGal, \%optsSys);

    # the final options hash.
    my %opts = mergeOpts(\%optsSys,\%optsRC,\%optsGal,\%optsCmd);

    # was the -h or --help option called?
    &printUsage if $opts{h};

    # was the -v or --version option called?
    &printVersion if $opts{v};

    # generate the albumInfo array
    my $albumInfo = &genAlbumInfo(\%opts,$dir);

    if (($#{$albumInfo->{images}} == -1) && (!($opts{cg}))) {
        print "No image files were found in \'$dir\'\nSkipping directory.\n";
    } elsif (!($opts{cg})) {
        # generate thumbnails
        &genThumbs(\%opts,$albumInfo);

        # generate slides
        &genSlides(\%opts,$albumInfo);

        # get the exif info
        &genExifInfo(\%opts,$albumInfo);

        # generate slide pages
        &genSlidePages(\%opts,$albumInfo);

        # generate info page
        &genInfoPages(\%opts,$albumInfo);

        # generate index page
        &genIndexPage(\%opts,$albumInfo);
    }


    # debug - print out the images and descriptions.
    if ($opts{d} >= 5) {
        print "--------------------------\n";       
        print "Album Title Bar: $albumInfo->{titlebar}\n";
        print "Album Title: $albumInfo->{title}\n";
        print "Album Header: $albumInfo->{header}\n";
        print "Album Footer: $albumInfo->{footer}\n";
        for $i (0 .. $#{$albumInfo->{images}}) {
            print "--------------------------\n";       
            print "images[$i]{file}: <$albumInfo->{images}[$i]->{file}>\n";
            print "images[$i]{desc}: <$albumInfo->{images}[$i]->{desc}>\n";
            print "images[$i]{size}: <$albumInfo->{images}[$i]->{size}>\n";
            print "images[$i]{width}: <$albumInfo->{images}[$i]->{width}>\n";
            print "images[$i]{height}: <$albumInfo->{images}[$i]->{height}>\n";
            print "images[$i]{thumb}: <$albumInfo->{images}[$i]->{thumb}>\n";
            print "images[$i]{thumbx}: <$albumInfo->{images}[$i]->{thumbx}>\n";
            print "images[$i]{thumby}: <$albumInfo->{images}[$i]->{thumby}>\n";
            print "images[$i]{slide}: <$albumInfo->{images}[$i]->{slide}>\n";
            print "images[$i]{slidex}: <$albumInfo->{images}[$i]->{slidex}>\n";
            print "images[$i]{slidey}: <$albumInfo->{images}[$i]->{slidey}>\n";
            print "images[$i]{slidekb}: <$albumInfo->{images}[$i]->{slidekb}>\n";
            if ($#{$albumInfo->{images}[$i]->{exif}} > 0) {
                for $j (0 .. $#{$albumInfo->{images}[$i]->{exif}}) {
                    print "  $j: $albumInfo->{images}[$i]->{exif}->[$j]{field} : ";
                    print "  $albumInfo->{images}[$i]->{exif}->[$j]{val}\n";
                }
            }
        }
    }

    # change back to our starting dir.
    die "Can't cd to $startDir $!\n" unless chdir $startDir;

}
print "\njGal has finished processing\n";
exit;


########################### [ Functions below ] ###############################
# getImgMgkVer - get the version of ImageMagic
#
# Sets the array reference to be the version of ImageMagick we have.
# There is output differences in the 'identify' program we need to consider
# which is why we need this.
#
sub getImgMgkVer {
   my $tmp = qx/$imgInfoProg/;    # run the 'identify' prog.
   my @tmpArr = split /\n/,$tmp;  # split on new line
   chop @tmpArr;                  # chop any remaining new line
   my @lineArr = split / /,$tmpArr[0]; # split up the first line of the output
   return split /[.]/,$lineArr[2]; # split and return the version field X.y.z
}

# getDirs - get the list of directories we are going to run the program on.
#
# If no directories are given on the command line then we assume the
# current directory.
#
# Returns a list.
#
sub getDirs {
    # save the ARGV option because the GetOptions function will modify
    # it and we need it intact for later.
    my @tmpArgv = @ARGV;
    my @tmpDirs = ();

    # call getCmdOpts, but we don't want the output, just the resulting
    # ARGV list.
    &getCmdOpts ({});

    # check to see if any dirs we're on the cmdline or not.
    if ($#ARGV == -1) {
        @tmpDirs = (".");
    } else {
        @tmpDirs = @ARGV;
    }

    # retrore the orginal args
    @ARGV = @tmpArgv;

    return @tmpDirs;
}

###########################################################
# setSystemOpts - setup the system-wide option defaults
# 
# returns a hash which contains all the option and value pairs
# the key values MUST be the same as the short option name!!
#
sub setSystemOpts {
    return ("cg"=>"0",   # create-gallerydat
            "rs"=>"a",   # replace-spaces (default ask)
            "ft"=>"0",   # force-thumb
            "fs"=>"0",   # force-slide
            "io"=>"0",   # index-only
            "iw"=>"5",   # index-width
            "skb"=>"1",  # slide-kbsize
            "sxy"=>"1",  # slide-xysize
            "uo"=>"0",   # use-original
            "lo"=>"1",   # link-original
            "sy"=>"480", # slideY
            "sx"=>"0",   # slideX
            "ty"=>"75",  # thumbY
            "tx"=>"0",   # thumbX
            "ibg"=>"ffffff",  # index-background
            "ifg"=>"000000",  # index-forground
            "sbg"=>"ffffff",  # slide-background
            "sfg"=>"000000",  # slide-forground
            "h"=> "0",   # display help
            "v"=> "0",   # display version
            "d"=> "0"    # debug level
           );
}

###########################################################
# getRCOpts - get any options from the users .jGalrc file.
# There may be comments in the file (line starting with '#')
# Only the last line with options on it will be used.
#
# returns a hash
sub getRCOpts {

    # check to see if the rc file exists.
    if (-e $jGalRCFile) {
        print "Found \'$jGalRCFile\' -  Parsing options.\n";
    } else {
        print "No \'$jGalRCFile\' resource file found.\n"; 
        return ();
    }
    open RCFILE, "<$jGalRCFile" or die "Cannot open \'$jGalRCFile\'\n";

    # store the argv array temporarily
    my @tmpARGV = @ARGV;

    # get the options from the file and run them through the command line
    # option checker the ensure validity.
    my $line = "";
    while (<RCFILE>) {
        if ($_ =~ /^#/) {
            # skip - line is not an option
        } elsif ($_ =~ /^[ ]*$/) {
            # skip - line is blank
        } else {
            $line = $_;
        }
    }
    close RCFILE; # close the rc file

    chop $line;                # strip off that pesky newline
    @ARGV = split / /,$line;   # move our options to the ARGV variable
    my %opts = getCmdOpts({}); # retrun a nice hash of options
    @ARGV = @tmpARGV;          # restore the original ARGV array.

    # return the options hash for the RC file
    return %opts;
}

###########################################################
# cgetGalOpts - get any options that are stored in gallery.dat
# Any line that starts with "GAL-OPTIONS" is considered an option argument
# and will be used.
#
# returns a hash
sub getGalOpts {

    # check to see if the gallery.dat file exists.
    if (-e $galDatFile) {
        print "Found \'$galDatFile\' - Parsing options.\n";
    } else {
        print "No \'$galDatFile\' resource file found.\n"; 
        return ();
    }
    open DATFILE, "<$galDatFile" or die "Cannot open \'$galDatFile\'\n";

    # store the argv array temporarily
    my @tmpARGV = @ARGV;

    # get the options from the file and run them through the command line
    # option checker the ensure validity.
    @tmpArr = (); # temporary array
    while (<DATFILE>) {
       # build up an array of args found in the gallery.dat file
       if ($_ =~ /^GAL-OPTIONS/) {
          chop $_;                   # strip off that pesky newline
          my @tmpOpt = split / /,$_; # make an array of the option
          shift @tmpOpt;             # remove the tag from the array
          push @tmpArr,@tmpOpt;      # add our new option to the array
       }
    }
    close DATFILE; # close the gallery.dat file

    @ARGV = @tmpArr;                # move our options to the ARGV variable
    my %opts = getCmdOpts({}); # retrun a nice hash of options
    @ARGV = @tmpARGV;          # restore the original ARGV array.

    # return the options hash for the gallery.dat file
    return %opts;
}

###########################################################
# getCmdOpts - get the command line options
#
# uses the system default hash to populate the command line hash. Any new
# options that were passed from the command line will be updated here.
# returns a new hash that contains all the options for the program.
#
# When the procedure is done the value of @ARGV will have changed. All of
# the options will have been removed leaving just the remaining, non-options,
# left. In our case these will be the directories we want to run the program
# on.
#
sub getCmdOpts {
    # populate optsCmd with the hash passed in. This way we don't have to
    # predefine all the keys for the hash again.
    my (%opts) = %{$_[0]};

    # get the options and store them in the optsCmd hash.
    die unless GetOptions(\%opts, 'cg|create-gallerydat',
               'rs|replace-spaces=s',
               'ft|force-thumb!',
               'fs|force-slide!',
               'io|index-only!',
               'iw|index-width=i',
               'skb|slide-kbsize!',
               'sxy|slide-xysize!',
               'uo|use-original!',
               'lo|link-original!',
               'sy|slideY=i',
               'sx|slideX=i',
               'ty|thumbY=i',
               'tx|thumbX=i',
               'ibg|index-background=i',
               'ifg|index-forground=i',
               'sbg|slide-background=i',
               'sfg|slide-forground=i',
               'h|help',
               'v|version',
               'd|debug=i'
              );

    # return the optsCmd hash
    return %opts;
}

###########################################################
# checkOpts - check the validity of the options hash.
#
# Do bounds checking for options that require an argument.
# Print error and set option to default if an option is bad.
# 
# return the new options hash
#
sub checkOpts {
    my (%opts)    = %{$_[0]}; # options to check
    my (%optsSys) = %{$_[1]}; # system defaults

    # bounds check the options
    # --replace-spaces
    if ((defined $opts{rs}) and !($opts{rs} =~ /^[ayn]$/i)) {
        print "Error: '$opts{rs}' is an invalid value to option --replace-spaces\n";
        print "  Valid values are [ayn] where 'a'=ask; 'y'=yes; 'n'=no\n";
        print "  Resetting option to default: '$optsSys{rs}'\n";
        $opts{rs} = $optsSys{rs};
    }
    # --index-width
    if ((defined $opts{iw}) and (($opts{iw} <= 0) or ($opts{iw} > 255))) {
        print "Error: '$opts{iw}' is an invalid value to option --index-width\n";
        print "  Valid values are 1 - 255\n";
        print "  Resetting option to default: '$optsSys{iw}'\n";
        $opts{iw} = $optsSys{iw};
    }
    # --slideY
    if ((defined $opts{sy}) and $opts{sy} <= 0) {
        print "Error: '$opts{sy}' is an invalid value to option --slideY\n";
        print "  Minimum value is 1\n";
        print "  Resetting option to default: '$optsSys{sy}'\n";
        $opts{sy} = $optsSys{sy};
    }
    # --slideX
    if ((defined $opts{sx}) and $opts{sx} <= 0) {
        print "Error: '$opts{sx}' is an invalid value to option --slideX\n";
        print "  Minimum value is 1\n";
        print "  Resetting option to default: '$optsSys{sx}'\n";
        $opts{sx} = $optsSys{sx};
    }
    # --thumbY
    if ((defined $opts{ty}) and $opts{ty} <= 0) {
        print "Error: '$opts{ty}' is an invalid value to option --thumbY\n";
        print "  Minimum value is 1\n";
        print "  Resetting option to default: '$optsSys{ty}'\n";
        $opts{ty} = $optsSys{ty};
    }
    # --thumbX
    if ((defined $opts{tx}) and $opts{tx} <= 0) {
        print "Error: '$opts{tx}' is an invalid value to option --thumbX\n";
        print "  Minimum value is 1\n";
        print "  Resetting option to default: '$optsSys{tx}'\n";
        $opts{tx} = $optsSys{tx};
    }

    # do any options specific processing

    return %opts;
}

###########################################################
# mergeOpts - merge the various options arrays into one single array.
#
# merger order is:
# system options -> ~/.jGalrc -> gallery.dat -> command line options.
#
# return a new hash with the final options defined
#
sub mergeOpts {
    my ($sys,$rc,$gal,$cmd) = @_;

    # start with the system options and merge the rc options over them
    my %opts = ();
    foreach $key (keys %$sys) {
        if (defined $$rc{$key}) {
            # use the rc option
            $opts{$key} = $$rc{$key};
        } else {
            # keep the system default
            $opts{$key} = $$sys{$key};
        }
    }

    # now merge the gallery options over the these
    foreach $key (keys %opts) {
        if (defined $$gal{$key}) {
            # use the gallery option
            $opts{$key} = $$gal{$key};
        }
    }

    # finally merge the command line options over the these
    foreach $key (keys %opts) {
        if (defined $$cmd{$key}) {
            # use the command line option
            $opts{$key} = $$cmd{$key};
        }
    }

    # debug - print all the options and when the got set.
    if ($opts{d} >= 2) {
        print "\nFinal Options:\nKey:\tSys\tRC\tGal\tCmd\tOpts\n";
        foreach $key (keys %$sys) {
            print "$key:\t$$sys{$key}\t";
            if (defined $$rc{$key}) {
                print "$$rc{$key}\t"
            } else {
                print "--\t";
            }
            if (defined $$gal{$key}) {
                print "$$gal{$key}\t"
            } else {
                print "--\t";
            }
            if (defined $$cmd{$key}) {
                print "$$cmd{$key}\t"
            } else {
                print "--\t";
            }
            print "$opts{$key}\n";
        } 
        print "\n";
    }

    return %opts;
}

###########################################################
# genAlbumInfo - generate the master array containing info about the album.
#
# options that concern this function:
# -cg (--create-gallerydat)
# -rs (--replace-spaces)
#
# -cg option not set
# ------------------
# gallery.dat file exists:
# It will parse it and return a list of hashes containing the image file
# names and descriptions. The gallery.dat file will NOT be written to.
#
# gallery.dat file does not exists: 
# It will simply return the list of hashes with the file names filled in
# and the descriptions empty. NO gallery.dat file will be created!
#
# -cg option set
# --------------
# gallery.dat file exists OR gallery.dat file does not exist:
# It will create a NEW gallery.dat file and return an empty array.
# That means if the gallery.dat file exists, any options, file descriptions
# or file ordering will be LOST!
# 
# --------------------------
# returns a reference to a hash.
# --------------------------
# The hash contains the title of the page, the header and footer strings
# and a reference to an array of images.
# The array of images is ordered in the way the files are listed in
# gallery.dat.  If no gallery.dat file is found, it will store the files
# in the order that glob retuned them. 
#
sub genAlbumInfo {
    my (%opts)  = %{$_[0]};  # options hash
    my ($dir)   = $_[1];     # the directory we're working on
    my $href    = {};        # temp hash reference
    my $aInfo   = &newAlbumInfo; # album info hash
    my @tmpArr = ();         # temp array
    my $picCnt = 0;          # count of valid pictures
    my $skipCnt = 0;         # count of skipped pictures
    my $filesWithSpaces = 0; # number of files with spaces

    print "Retreiving file info from directory \'$dir\'\n" if $opts{d};

    # if we have a gallery.dat file and we're not creating a new
    # gallery.dat file.
    if (-e $galDatFile && !$opts{cg}) {
        # read the gallery.dat file and update the albumInfo hash
        open GALFILE, "<$galDatFile" or die "Cannot open \'$galDatFile\'\n";

        print "Found \'$galDatFile\' - Retreiving file list.\n";

        while (<GALFILE>) {
            if ($_ =~ /^#/) {
                # skip - line is a comment
            } elsif ($_ =~ /^[ ]*$/) {
                # skip - line is blank
            } elsif ($_ =~ /^GAL-OPTIONS/) {
                # skip - line contains gallery options
            } elsif ($_ =~ /^INDEX/) {
                # we have some index option
                chop $_;

                # read in the index tag and it's value and
                # trim off the whitespace
                ($tag,$val) = split /----/,$_;
                &trimWS($tag); &trimWS($val);
                if ($tag =~ /index-titlebar/i) {
                    $aInfo->{titlebar} = $val; 
                } elsif ($tag =~ /index-title/i) {
                    $aInfo->{title} = $val; 
                } elsif ($tag =~ /index-header/i) {
                    $aInfo->{header} = $val; 
                } elsif ($tag =~ /index-footer/i) {
                    $aInfo->{footer} = $val; 
                } else {
                    print "Warning: Invalid option found in $galDatFile. <$tag>\n";
                }
            } else {
                # we have a filename and description line
                $href = &newImgInfo; # create a new image info hash
                chop $_;             # get rid of that pesky newline

                # read in the file name and descr
                ($file,$desc) = split /----/,$_;

                # skip file if it doesn't exist
                if (!-e trimWS($file)) {
                    print "Skipping nonexistent image file: $file\n";
                    $skipCnt++;
                    next;
                }

                # stuff the file and desc into the hash
                $href->{file} = trimWS($file);
                $href->{desc} = trimWS($desc);

                # get the files size and X,Y info
                @tmpArr = &getImgInfo($href->{file});
                $href->{size} = $tmpArr[0];
                $href->{width} = $tmpArr[1];
                $href->{height} = $tmpArr[2];

                # push the new image into the array if the image is valid
                if ($href->{size} ne "") {
                    push @{$aInfo->{images}},$href;
                    # my $spaceCnt = () = $file =~ m/ /g;
                    $filesWithSpaces++ if ($href->{file} =~ / /);
                    $picCnt++;
                } else {
                    print "Skipping invalid image file: $file\n";
                    $skipCnt++;
                }
            }
        }
        close GALFILE; # close the gallery.dat file
        print "$picCnt of " . ($picCnt + $skipCnt) . " image files found in $galDatFile were valid.\n";

    } else {
        # no gallery.dat file found - just using image files found in dir.
        # get a list of files to work on
        my @files = glob($fileTypeStr);

        if ($opts{cg}) {
            print "Creating a new $galDatFile. Checking for files in directory.\n";
        } else {
            print "No $galDatFile file found. Checking for files in directory.\n";
        }

        # stuff each file into the hash and push it on the list
        foreach $file (@files) {
            # create a new imgInfo structure and fill in it's name
            $href = &newImgInfo;
            $href->{file} = trimWS($file); 

            # get the files size and X,Y info
            @tmpArr = &getImgInfo($href->{file});
            $href->{size} = $tmpArr[0];
            $href->{width} = $tmpArr[1];
            $href->{height} = $tmpArr[2];

            # push the new image into the array if the image is valid
            if ($href->{size} ne "") {
                push @{$aInfo->{images}},$href;
                $filesWithSpaces++ if ($href->{file} =~ / /);
                $picCnt++;
            } else {
                print "Skipping invalid image file: $file\n";
                $skipCnt++;
            }
        }

        print "$picCnt of " . ($picCnt + $skipCnt) ." image files found in the directory were valid.\n";

    }

    # replace spaces in filenames with underscores
    &convertFileNames($aInfo,$opts{rs}) if $filesWithSpaces;

    # create a new gallery.dat file from the files hash and return
    if ($opts{cg}) {
        &createNewGalDat(\%opts, $aInfo);
        return ();
    }

    return $aInfo;
}

###########################################################
# convertFileNames - replace spaces with underscores in filenames
#
# Input: albumInfo reference and -rs option value
#        
sub convertFileNames {
    my ($albumInfo,$opt) = @_;
    my $yn = "";     # temp yes/no variable
    my $dieMsg = ""; # message to send to die 
    
    # while we don't have a yes or no answer from the user
    while (!($opt =~ /^[yn]$/i)) {
        print "\n----------------------------------------------------\n";
        print "There were spaces detected in some of the filenames.\n";
        print "Spaces are EVIL, hard to work with, can break programs and\n";
        print "are a pain in the ass to deal with in HTML code.\n";
        print "It's recommended that you convert the spaces to underscores\n";
        print "in the filenames.\n";
        print "You can set the -rs|--replace-spaces option and you'll never\n";
        print "see this message again.\n";
        print "\n";
        print "Do you want jGal to convert the offending file names?\n";
        print "(y) or n > ";
        $yn = <STDIN>; # get the input from the user
        chop $yn;
        $yn = "y" if $yn eq ""; # if they hit enter, make it a "y"

        # if $yn is a y or n, set opt to it and quit the loop.
        $opt = $yn if $yn =~ /^[yn]$/i;
    } 

    # if we had a yes response, convert the filenames
    if ($opt eq "y") {
        print "Converting spaces in filenames to underscores.\n";
        # check each filename
        my $orig = ""; # original file name
        my $new  = ""; # new file name
        for $i (0 .. $#{$albumInfo->{images}}) {
            $orig = $albumInfo->{images}[$i]->{file};
            $albumInfo->{images}[$i]->{file} =~ s/ /_/g;
            $new = $albumInfo->{images}[$i]->{file};
            # only move the file if it's different
            if (!($orig eq $new)) {
                $dieMsg = "Cannot move $orig to $new\n";
                move $orig,$new or die "$dieMsg $!\n";
            }
        }

        # change the filenames in the gallery.dat file too.
        if (-e $galDatFile) {
            print "Updating $galDatFile\n";

            # move the gallery.dat file to a temp file
            my $tmpGalDatFile = "tmp-$galDatFile";
            my $dieMsg = "Cannot move $galDatFile to $tmpGalDatFile!\n";
            move $galDatFile,$tmpGalDatFile or die "$dieMsg$!\n";

            # open for temp file for reading, original file for writing
            open RFILE, "<$tmpGalDatFile" or die "Cannot open \'$tmpGalDatFile\'\n";
            open WFILE, ">$galDatFile" or die "Cannot open \'$galDatFile\'\n";

            # read in each line and change if need be
            while (<RFILE>) {
                if (($_ =~ /^#/) or ($_ =~ /^$/) or ($_ =~ /^GAL-OPTIONS/) or ($_ =~ /^INDEX/)) {
                    # just write - line is a comment,blank or contains options
                    print WFILE $_;
                } else {
                    # we have a filename and description line
                    chop $_; # get rid of that pesky newline
       
                    # read in the file name and descr
                    ($file,$desc) = split /----/,$_;
                    trimWS($file);    # trim whitespace
                    trimWS($desc);    # trim whitespace
                    $file =~ s/ /_/g; # replace the spaces with underscores
                    
                    # write the line back to the file
                    print WFILE "$file ---- $desc\n";
                }
            }
            # close the files
            close RFILE;
            close WFILE;

            # delete the temp file
        }
    }
}

###########################################################
# getImgInfo - takes file name and returns an array with the
# size, width, and height of the image (in that order).
#
sub getImgInfo {
    my ($file) = @_;
    my @retArr = ();
    my $badImgMsg = "identify:";

    # count the number of spaces in the filename for offset reasons
    my $spaceCnt = () = $file =~ m/ /g;

    # get file size, and XxY info
    my $line = qx/$imgInfoProg -ping "$file" 2>&1/;
    chop $line; # remove pesky new line

    # check to see if we had a bad file
    if ($line =~ m/$badImgMsg*/i) {
        # bad image found.
        # push empty values into the array to return
        push @retArr,"";
        push @retArr,"";
        push @retArr,"";

    } else {
        # pick out the XxY portion of the line
        my $xyLine = $1 if ($line =~ m/([0-9]+x[0-9]+)/);
        my @xyArr  = split /x|[+]|[=>]/,$xyLine; # split up XxY+val+val
        my $width  = &trimWS($xyArr[0]);
        my $height = &trimWS($xyArr[1]);
        # pick out the size portion of the line
        my $size = $1 if ($line =~ m/([0-9]+[kb]+[b]*)/);
        $size = &sizeToKb($size);

        # push the values into the array to return
        push @retArr,$size;
        push @retArr,$width;
        push @retArr,$height;
    }
    return @retArr;
}

###########################################################
# sizeToKb - make sure a size is in kb and not bytes.
#
sub sizeToKb {
    my ($size) = @_;

    return $size if ($size =~ /kb/); # return if we already have kb
    return $size if ($size eq "");   # return if there was no size
    return $size if ($size eq "0b"); # return if the size was 0

    # remove the 'b' char at the end of the string
    $size =~ s/b//; 

    # round (not trunc) the division to the nearest int and add kb at the end
    $size = sprintf("%.0fkb",($size / 1024));

    return $size;
}

###########################################################
# createNewGalDat - create a new gallery.dat file in the
# current directory from the list of valid filenames in the albumInfo
#
sub createNewGalDat {
    my (%opts) = %{$_[0]};
    my ($albumInfo) = $_[1];
    my $yn = "y"; # create galDatFile

    if (-e $galDatFile) {
        $yn = ""; # clear this option out so we can ask the user

        # while we don't have a yes or no answer from the user
        while (!($yn =~ /^[yn]$/i)) {
            print "\n----------------------------------------------------\n";
            print "You have called $progName with the --create-gallerydat option\n";
            print "$progName has detected that a $galDatFile already exists.\n";
            print "If you overwrite this file all current descriptions and\n";
            print "customizations will be LOST!!\n";
            print "\nWould you like to overwrite the current $galDatFile?\n";
            print "y or (n) > ";
            $yn = <STDIN>; # get the input from the user
            chop $yn;
            $yn = "n" if $yn eq ""; # if they hit enter, make it a "n"
        }
        print "----------------------------------------------------\n\n";
    }

    # if we wanted to 
    if ($yn eq "y") {
        print "Creating $galDatFile\n";

        open(GALFILE,"> $galDatFile") || die "Cannot open \'$galDatFile\' $!\n";
        print GALFILE "# Options can be placed after GAL-OPTIONS tag.\n";
        print GALFILE "# They should be on one line and entered like they would\n";
        print GALFILE "# on the command line. e.g. GAL-OPTIONS -ft -fs -rs y\n";
        print GALFILE "GAL-OPTIONS\n";
        print GALFILE "#\n";
        print GALFILE "##############################################\n";  
        print GALFILE "# Below are the index options with their values and\n";
        print GALFILE "# the file names and descriptions\n";
        print GALFILE "# All are seperated by '----' this MUST remain.\n";
        print GALFILE "# The file name and description must be on ONE line. The\n";
        print GALFILE "# description can contain html tags if desired.\n";
        print GALFILE "# You can change the order and it will be preserved when\n";
        print GALFILE "# you generate the index.html and slide pages.\n";  
        print GALFILE "##############################################\n";  
        print GALFILE "#\n";  
        print GALFILE "INDEX-TITLEBAR ---- My Pictures\n";
        print GALFILE "INDEX-TITLE ---- My Pictures\n";
        print GALFILE "INDEX-HEADER ----\n";
        print GALFILE "INDEX-FOOTER ----\n";
        print GALFILE "#\n";
        for $i (0 .. $#{$albumInfo->{images}}) {
            print GALFILE "$albumInfo->{images}[$i]->{file} ----\n";
        }
        close GALFILE;
    } else {
        print "Skipping creation of $galDatFile\n";
    }
}

###########################################################
# genThumbs - generates thumbnail files
# 
# input: a reference to the options hash
#        a reference to the albumInfo
#
# side effect: update the albumInfo with the name of the thumbnail image
#
sub genThumbs {
    my (%opts) = %{$_[0]};
    my ($albumInfo) = $_[1];
    print "Generating thumbnails\n";

    my $tmpFile = "";          # temp variable to hold image name
    my $tmpThumbFile = "";     # temp variable to hold thumbnail name
    my @tmpArr = ();           # temp array
    my $cmd  = "$scaleProg"; # cmd to run to scale images
    my $dieMsg = "";           # message to print if we die
    my $msgPad = "                    "; # 20 spaces
    my $genCnt = 0;            # number of thumbnails generated
    my $skipCnt = 0;           # number of thumbnails already existing

    # create the thumbnail directory if need be
    if (!(-d $thumbDir)) {
        print "The thumbnail directory: '$thumbDir' did not exist. Creating.\n";
        $dieMsg = "Cannot create the directory '$thumbDir'.\n";
        mkdir $thumbDir or die $dieMsg;
    }

    # generate thumbnail for each image
    for $i (0 .. $#{$albumInfo->{images}}) {
        # get the name of the file and create the thumbnail filename
        # store the name of the thumbnail for this image in the albumInfo
        $tmpFile = $albumInfo->{images}[$i]->{file};
        $tmpThumbFile = "$thumbPrefix" . "$tmpFile";
        $albumInfo->{images}[$i]->{thumb} = $tmpThumbFile;

        # copy and scale if the thumbnail does not already exist or we are
        # forcing the generation of the thumbnails
        if ($opts{ft} or (!(-e $tmpThumbFile))) {
            # scale the image to the thumbnail size specs
            print "\r\(" . ($i+1) . "/" . ($#{$albumInfo->{images}}+1) . "\) Scaling $tmpThumbFile $msgPad";
            $cmd = "$scaleProg -scale x$opts{ty} -sharpen 5 \"$tmpFile\" \"$tmpThumbFile\"";
            $dieMsg = "\nCannot scale the thumbnail image $tmpThumbFile!\n";
            system($cmd) == 0 or die $dieMsg;

            # increment the generated count
            $genCnt++;
        } else {
            # increment the skipped count
            print "\r\(" . ($i+1) . "/" . ($#{$albumInfo->{images}} + 1) . "\) Skipping $tmpThumbFile $msgPad";
            $skipCnt++;
        }

        # get the files size and X,Y info for the slide
        @tmpArr = &getImgInfo($albumInfo->{images}[$i]->{thumb});
        $albumInfo->{images}[$i]->{thumbx} = $tmpArr[1];
        $albumInfo->{images}[$i]->{thumby} = $tmpArr[2];
    }
    print "\r"; # move back to the start of the line
    print "$genCnt thumbnails generated. $msgPad\n" if $genCnt > 0;
    print "$skipCnt thumbnails skipped because they already existed.\n" if $skipCnt > 0;
    print "Finished generating thumbnail images.\n\n";
}

###########################################################
# genSlides - generates slide files
#
# input: a reference to the options hash
#        a reference to the albumInfo
#
# side effect: update the albumInfo with the name of the slide image
#              update the albumInfo with the size in kb of the slide image
#              update the albumInfo with the XxY dimensions of the slide image
#
sub genSlides {
    my (%opts) = %{$_[0]};
    my ($albumInfo) = $_[1];
    print "Generating slides\n";

    my $tmpFile = "";          # temp variable to hold image name
    my $tmpSlideFile = "";     # temp variable to hold slide name
    my $cmd  = "$scaleProg"; # cmd to run to scale images
    my $dieMsg = "";           # message to print if we die
    my $msgPad = "                    "; # 20 spaces
    my $genCnt = 0;            # number of thumbnails generated
    my $skipCnt = 0;           # number of thumbnails already existing
    my $tmpArr = ();           # temporary array

    # create the slide directory if need be
    if (!(-d $slideDir) && !($opts{uo})) {
        print "The slide directory: '$slideDir' did not exist. Creating.\n";
        $dieMsg = "Cannot create the directory '$slideDir'.\n";
        mkdir $slideDir or die $dieMsg;
    }

    # generate slide for each image
    for $i (0 .. $#{$albumInfo->{images}}) {
        # get the name of the file and create the slide filename
        $tmpFile = $albumInfo->{images}[$i]->{file};
        $tmpSlideFile = "$slidePrefix" . "$tmpFile";

        # store the name of the slide for this image in the albumInfo
        if ($opts{uo}) {
            # if the use-originals option is used, we're not going to
            # create a slide
            $albumInfo->{images}[$i]->{slide} = $tmpFile;
        } else {
            # sotore the slide file if making slides
            $albumInfo->{images}[$i]->{slide} = $tmpSlideFile;
        }

        # copy and scale if the slide does not already exist or we are
        # forcing the generation of the slides and we're not using the orig's
        if (($opts{fs} or (!(-e $tmpSlideFile))) && !($opts{uo})) {
            # scale the image to the slide size specs
            print "\r\(" . ($i+1) . "/" . ($#{$albumInfo->{images}}+1) ."\) Scaling $tmpSlideFile $msgPad";
            $cmd = "$scaleProg -scale x$opts{sy} -sharpen 5 \"$tmpFile\" \"$tmpSlideFile\"";
            $dieMsg = "\nCannot scale the slide image!\n";
            system($cmd) == 0 or die $dieMsg;

            # increment the generated count
            $genCnt++;
        } else {
            # increment the skipped count
            print "\r\(" . ($i+1) . "/" . ($#{$albumInfo->{images}}+1) . "\) Skipping $tmpSlideFile $msgPad";
            $skipCnt++;
        }
        
        # get the image info for the slide
        if ($opts{uo}) {
            # we're using the originals and already have the
            # size, X and Y info of the originals. use those.
            $albumInfo->{images}[$i]->{slidekb} = $albumInfo->{images}[$i]->{size};
            $albumInfo->{images}[$i]->{slidex} = $albumInfo->{images}[$i]->{width};
            $albumInfo->{images}[$i]->{slidey} = $albumInfo->{images}[$i]->{height};
        } else {
            # get the files size and X,Y info for the slide
            @tmpArr = &getImgInfo($albumInfo->{images}[$i]->{slide});
            $albumInfo->{images}[$i]->{slidekb} = $tmpArr[0];
            $albumInfo->{images}[$i]->{slidex} = $tmpArr[1];
            $albumInfo->{images}[$i]->{slidey} = $tmpArr[2];
        }
    }

    print "\r"; # move back to the start of the line
    print "$genCnt slides generated. $msgPad\n" if $genCnt > 0;
    print "$skipCnt slides skipped because they already existed.\n" if $skipCnt > 0;
    print "Finished generating slide images.\n\n";
}

###########################################################
# genExifInfo - generate the exif info for each jpeg image
#
# generates exif info for all jpg images.
#
# input:
# reference to the options hash
# reference to album info hash
#
sub genExifInfo {
    my (%opts) = %{$_[0]};   # options hash
    my ($albumInfo) = $_[1]; # album info
    print "Generating EXIF info\n";

    # generate page for each image
    for $i (0 .. $#{$albumInfo->{images}}) {
        # get the name of the file
        $tmpFile = $albumInfo->{images}[$i]->{file};

        # set the exif info for the image
        $albumInfo->{images}[$i]->{exif} = &getExifInfo($tmpFile);
    }
}

###########################################################
# getExifInfo - run the exif program, and get the exif info for an image.
#
# This runs the users exif extraction program on an image creating an
# array of hashes. Each array element is a hash which corresponds to each
# line in the output of the exif program.  Each hash has two keys, 'field'
# and 'val'.
# 
# jhead - the exif extraction program output in the form:
# exif field : exif value
# the 'field' value corresponds to everything before the first ':'.
# the 'val' value corresponds to everything after the first ':'.
#
# all leading and trailing whitespace is removed from both field and val.
#
# input:  the file we want to get exif info from.
# return: the reference to the array of hashes.
#
sub getExifInfo {
    my ($file) = @_;

    my @exifArr = ();

    # exif info only exits in jpeg files. Return if not.
    return \@exifArr unless ($file =~ /jpe?g/i);

    # get the output of the exif program
    my $cmdOutput = qx/$exifProg "$file"/;

    # split it up into lines and parse each line
    my @cmdArr = split /\n/,$cmdOutput;
    foreach $line (@cmdArr) {
        # clear out the temporary hash
        my %tmpHash = (field => "", val => "");

        # split on the field delimeters
        @lineArr = split /:/,$line;

        # add in the field value
        $tmpHash{field} = trimWS($lineArr[0]);
        shift @lineArr; # remove the first element

        # rejoin the line array to preserve any other ':'s in the line
        $line = join ':',@lineArr;
        # add the value to the hash
        $tmpHash{val} = trimWS($line);

        # stuff a reference to the hash at the end of the array
        push @exifArr,\%tmpHash;
    } 

    return \@exifArr;
}

###########################################################
# newAlbumInfo - returns a hash structure containing all the
# info about the album.
sub newAlbumInfo {
    return {
        titlebar =>  "My Pictures", # title bar of album
        title =>  "My Pictures", # title of album
        header => "Header Info", # header string
        footer => "Footer Info", # footer string
        images => []  # array of images - created later
    };
}

###########################################################
# newImgInfo - returns a hash structure containing all the
# info about one file.
sub newImgInfo {
    return {
        file => "",    # string - filename
        desc => "",    # string - description
        width => "",   # images width (in pixles)
        height => "",  # images height (in pixles)
        size => "",    # file size of image in Kb.
        thumb => "",   # filename of thumbnail image
        thumbx => "",  # width of thumbnail image
        thumby => "",  # height of thumbnail image
        slide => "",   # filename of slide image
        slidekb => "", # size in kb of slide the image
        slidex => "",  # width of slide image
        slidey => "",  # height of slide image
        exif => {}     # hash reference - created later
    };
}

###########################################################
# trimWS - from begining and end of input
#
sub trimWS
{
    return "" if (!(defined $_[0]));
    $_[0] =~ s/^\s+|\s+$//go ;
    return $_[0];
}

###########################################################
# printVersion - prints the version info and exists
#
sub printVersion {
    print "\nJason's Gallery - jGal\n";
    print "Version $version (c)2002 $author\n";
    print "Please run '$progName -h' for help\n\n";
    exit;
}

###########################################################
# printUsage - print the usage and exit
#
sub printUsage {
print <<endOfPrint;
Usage: $progName [options] [directories]
[options]
* -cg  --create-gallerydat
                    : create a gallery.dat file and exit
* -rs  --replace-spaces <a|y|n>
                    : replaces spaces with _'s in filenames
                      a - (default) Ask the user if they want to
                      y - replace and do no ask
                      n - do not replace and do no ask
* -ft  --force-thumb  : force thumbnail regeneration (can be negated)
* -fs  --force-slide  : force slide regeneration (can be negated)
-io  --index-only   : only create an index page - do not generate slides
                      Default is DISABLED. (can be negated)
* -skb --slide-kbsize : print the file size of the slide on the index page
                      under the thumbnails. Default is ENABLED (can be negated)
* -sxy --slide-xysize : print the slide dimensions under the thumbnails 
                      on the index page. Default is ENABLED (can be negated)
* -uo --use-original  : use the original images for the slides - do not
                      generate a scaled slide image. Default is DISABLED.
                      (can be negated)
* -lo --link-original : link to the original images to the slides. Default
                      is ENABLED. (can be negated).
* -sy --slideY <int>  : size of the scaled slide image in the Y direction
                      default is 640 pixles
-sx --slideX <int>  : size of the scaled slide image in the X direction
                      no default.
* -ty --thumbY <int>  : size of the scaled thumbnail image in the Y direction
                      default is 75 pixles
-tx --thumbX <int>  : size of the scaled thumbnail image in the X direction
                      no default.
-sbg --slide-background <rrggbb> 
                    : background color of the slide pages in hex
                      default is ffffff
-sfg --slide-forground <rrggbb>
                    : forgroung color of the slide pages in hex
                      default is 000000
-ibg --index-background <rrggbb>
                    : background color of the index page in hex
                      default is ffffff
-ifg --index-forground <rrggbb>
                    : forground color of the index page in hex
                      default is 000000
* -iw --index-width <1-255>
                    : how many thumbnails per line you want on the
                      index page. Default is 5
* -h --help           : display this information and exit
* -v --version        : display version and exit
* -d --debug <0-5>    : set debug level. Default is 0

Note: If an option listed says it can be negated you can prefix it with "no"
      and the opposite effect will happen. Useful for overriding options
      on the command line that are set elsewhere. e.g. -nouo | --nouse-original
      will NOT use the original files as slides

[directories]
default directory is "."

Options can be used in any of three places: (listed ascending precedence)
- A single line in the .jGalrc file in the users home directory.
- After the GAL-OPTIONS tag as a single line in the gallery.dat file in
  each of the album directories.
- On the command line.
Regardless of where they are located, they should all be listed in the same
form as you would use them on the command line. Example: -lo -iw 6 -noskb

endOfPrint

exit;
}

###########################################################
# genIndexPage - generate the index.html page
#
sub genIndexPage {
    my (%opts) = %{$_[0]};
    my ($albumInfo) = $_[1];

    my $row = 0;  # keep track of what row we're on
    my $col = 0;  # keep track of what image we're on 
    my $col1 = 0; # keep track of what imgae we're on for size and dimensions
    my $thumbName = ""; # tmp variable to store thumbnail name
    my $thumbX = "";    # tmp variable to store the thumbnails X dimension
    my $thumbY = "";    # tmp variable to store the thumbnails Y dimension
    my $slideX = "";    # tmp variable to store the slides X dimension
    my $slideY = "";    # tmp variable to store the slides Y dimension
    my $slideKb = "";   # tmp variable to store the size (in kb) of the slide 
    my $tmpVar = "";    # tmp variable
    
    print "Generating the index.html page\n";

    if (!(-e $indexTmpl)) {
        print "No $indexTmpl file found. Creating\n";
        &genIndexTemplate;
    }

    # open the index.html file for writing
    open(INDEX,">$indexFile") or die "Cannot open $indexFile $!\n";

    # open the template file for reading
    open(INDEXTMPL,"<$indexTmpl") or die "Cannot open $indexTmpl: $!\n";

    # read each line of the template file and do what's appropriate
    # for each tag found
    my $line = "";
    while (<INDEXTMPL>) {
        $line = $_;

        if ($line =~ /INDEX-TITLEBAR/) {
            $line =~ s/INDEX-TITLEBAR/$albumInfo->{titlebar}/g;

        }
        if ($line =~ /INDEX-TITLE/) {
            $line =~ s/INDEX-TITLE/$albumInfo->{title}/g;

        }
        if ($line =~ /TIME-STAMP/) {
            $tmpVar = localtime(time());
            $line =~ s/TIME-STAMP/$tmpVar/g;

        } 
        if ($line =~ /INDEX-HEADER/ and ($albumInfo->{header} eq "")) {
            # the header value is empty, so replace the tag with nothing
            $line =~ s/INDEX-HEADER//g;

        }
        if ($line =~ /INDEX-HEADER/ and !($albumInfo->{header} eq "")) {
            # the header value contains info so create the header table
            $tmpVar = "<!---->\n<!-- Header info below -->\n<!---->\n<p>\n";
            $tmpVar = $tmpVar . "<table bgcolor=\"#444466\" border=1 cellspacing=0 cellpadding=6>\n";
            $tmpVar = $tmpVar . "    <tr>\n        <td class=\"hdr_ftr\">\n";
            $tmpVar = $tmpVar . "$albumInfo->{header}\n";
            $tmpVar = $tmpVar . "        </td>\n    </tr>\n</table>\n";
           
            $line =~ s/INDEX-HEADER/$tmpVar/g;

        }
        if ($line =~ /INDEX-FOOTER/ and ($albumInfo->{footer} eq "")) {
            # the footer value is empty, so replace the tag with nothing
            $line =~ s/INDEX-FOOTER//g;

        }
        if ($line =~ /INDEX-FOOTER/ and !($albumInfo->{footer} eq "")) {
            # the footer value contains info so create the footer table
            $tmpVar = "<!---->\n<!-- Footer info below -->\n<!---->\n<p>\n";
            $tmpVar = $tmpVar . "<table bgcolor=\"#444466\" border=1 cellspacing=0 cellpadding=6>\n";
            $tmpVar = $tmpVar . "    <tr>\n        <td class=\"hdr_ftr\">\n";
            $tmpVar = $tmpVar . "$albumInfo->{footer}\n";
            $tmpVar = $tmpVar . "        </td>\n    </tr>\n</table>\n";
           
            $line =~ s/INDEX-FOOTER/$tmpVar/g;

        }
        if ($line =~ /PICTURES/) {
            # print out each row of images
            $row = 0;      # to keep track of what row we're on
            $donePics = 0; # flag to indicate we're out of pics
            $opts{iw} = 5 if $opts{iw} <= 0; # reset index-width of invalid
            $tmpVar = "";  # clear out the tmp variable

            # generate a new row while we have pictures left
            while (!$donePics) {

            $tmpVar = $tmpVar . "<table bgcolor=\"#444444\" border=1 cellspacing=0 cellpadding=3>\n";
            $tmpVar = $tmpVar . "    <tr>\n";
            $tmpVar = $tmpVar . "        <td><table bgcolor=\"#444444\" border=0 cellspacing=0 cellpadding=4>\n";
            $tmpVar = $tmpVar . "            <tr>\n";

                # print out the thumbnails up to the value of index-width
                $col = 0; # start at first column
                while ($col < $opts{iw} && !$donePics) {
                    # figure out what image to grab.
                    $imgIndex = ($opts{iw} * $row) + $col;
                    $thumbName = $albumInfo->{images}[$imgIndex]->{thumb};
                    # replace any spaces in the name with html friendly code
                    $thumbName =~ s/ /\%20/g;
                    $thumbX = $albumInfo->{images}[$imgIndex]->{thumbx};
                    $thumbY = $albumInfo->{images}[$imgIndex]->{thumby};

                    # is this the last of the pictures?
                    $donePics=1 if ($imgIndex == $#{$albumInfo->{images}});
                    $tmpVar = $tmpVar . "                <td valign=middle align=center>\n";
                    $tmpVar = $tmpVar . "                    <a href=\"" . ($imgIndex+1) . ".html\"><img src=\"$thumbName\" width=$thumbX height=$thumbY border=0></a></td>\n";

                    $col++; # increment the col count

                }
                $tmpVar = $tmpVar . "            </tr>\n";

                # print out the sizes and dimensions below the images
                if ($opts{skb} or $opts{sxy}) {

                    # start the new row for the size and dimension info
                    $tmpVar = $tmpVar . "            <tr>\n";

                    # print out each size/dimension up to the width of
                    # the row that was just generated
                    $col1 = 0;
                    while ($col1 < $col) {
                        $imgIndex = ($opts{iw} * $row) + $col1;
                        $slideX = $albumInfo->{images}[$imgIndex]->{slidex};
                        $slideY = $albumInfo->{images}[$imgIndex]->{slidey};
                        $slideKb = $albumInfo->{images}[$imgIndex]->{slidekb};

                        # only print out the fields the user asked us to
                        if ($opts{skb} and $opts{sxy}) {
                            # print both the size and dimensions
                            $tmpVar = $tmpVar . "                <td valign=middle align=center>" . $slideX . "x" . $slideY ." (" . $slideKb . ")</td>\n";
                        } elsif ($opts{skb} and !$opts{sxy}) {
                            # print just the size and dimensions
                            $tmpVar = $tmpVar . "                <td valign=middle align=center>(" . $slideKb . ")</td>\n";
                        } elsif (!$opts{skb} and $opts{sxy}) {
                            $tmpVar = $tmpVar . "                <td valign=middle align=center>" . $slideX . "x" . $slideY . "</td>\n";
                        }
                        $col1++; # increment the column1 count
                    }

                    # close up the size and dimensions row
                    $tmpVar = $tmpVar . "            </tr>\n";
                }
                
                # close up this row
                $tmpVar = $tmpVar . "        </table></td>\n";
                $tmpVar = $tmpVar . "    </tr>\n";
                $tmpVar = $tmpVar . "</table>\n";
                $tmpVar = $tmpVar . "<p>\n";
                $row++; # increment the row count
            }

            $line =~ s/PICTURES/$tmpVar/g;
        }

        # we're done processing this line,  print it to the file
        print INDEX $line;
    }

    close INDEX;     # close the index.html file
    close INDEXTMPL; # close the index_template file

}

###########################################################
# genIndexTemplate - generate the index template
#
sub genIndexTemplate {

    open(INDEXTMPL,">$indexTmpl") or die "Cannot open $indexTmpl: $!\n";
    
print INDEXTMPL <<endoftemplate;
<html>
<!-- The tag names in this template are used by jGal to insert html code. 
     The tags are in all capitals. They can be moved around in the template
     or deleted if desired. To see how this works just compare the template
     file with a generated index.html file. -->
<head>
  <title>INDEX-TITLEBAR</title>
</head>

<!---->
<!-- styles for the title, header and footer fonts -->
<!---->
<style>
    /* title */
    .title {
        font-family: Arial, Helvetica, sans-serif;
        font-size: 20pt;
        color: #dddddd;
        font-weight: normal;
    }
    /* header and footer */
    .hdr_ftr {
        font-family: verdana, sans-serif;
        font-size: 12;
        color: #dddddd;
        font-weight: normal;
    }
</style>

<body bgcolor="#333333" text="#dddddd" link="#95ddff" vlink="#aaaaaa">

<font face="verdana,sans-serif">

<!---->
<!-- title of page -->
<!---->
<table bgcolor="#444444" width=100% border=1 cellspacing=0 cellpadding=3>
    <tr>
        <td class="title">INDEX-TITLE</td>
    </tr>
</table>

INDEX-HEADER

<!---->
<!-- Tables with the index pictures below -->
<!---->
<p><br><center>
PICTURES
</center>

INDEX-FOOTER

<!---->
<!-- general page info below -->
<!---->
<br>
<table bgcolor="#444444" width=100% border=1 cellspacing=0 cellpadding=3>
    <tr>
        <td><table text="#111111" width=100% border=0 cellspacing=0 cellpadding=3>
            <tr>
                <td align=left class=hdr_ftr>Created on: TIME-STAMP</font></td>
                <td align=right class=hdr_ftr>Created with jGal!</font></td>
            </tr>
        </table></td>
    </tr>
</table>
</body>
</html>
endoftemplate

close INDEXTMPL;
}

###########################################################
# genSlidePages - generate the html slide pages
#
sub genSlidePages {
    my (%opts) = %{$_[0]};
    my ($albumInfo) = $_[1];

    my $slideFile = "";  # file name to create
    my $slideX = "";     # tmp variable to store the slides X dimension
    my $slideY = "";     # tmp variable to store the slides Y dimension
    my $curr0Index = ""; # 0 based index of this image
    my $curr1Index = ""; # 1 based index of this image
    my $prev0Index = ""; # 0 index of previous image
    my $prev1Index = ""; # 1 index of previous image
    my $next0Index = ""; # 0 index of next image
    my $next1Index = ""; # 1 index of next image
    my $tmpVar     = ""; # temp variable
    my $line       = ""; # variable to store current template line in
    
    print "Generating the slide html pages\n";

    if (!(-e $slideTmpl)) {
        print "No $slideTmpl file found. Creating\n";
        &genSlideTemplate;
    }

    # create one slide for each image
    for $curr0Index (0 .. $#{$albumInfo->{images}}) {
        # setup all the 0 and 1 based index values
        $curr1Index = $curr0Index + 1;
        $prev0Index = $curr0Index - 1;
        $prev1Index = $curr0Index;
        $next0Index = $curr1Index;
        $next1Index = $curr1Index + 1;

        # handle some special cases
        if ($curr0Index == 0) {
            # this is the first file.
            # the previous index will be the last index
            $prev0Index = $#{$albumInfo->{images}};
            $prev1Index = $#{$albumInfo->{images}} + 1;
        }
        if ($curr0Index == $#{$albumInfo->{images}}) {
            # this is the last file.
            # the next index will be the first index
            $next0Index = 0;
            $next1Index = 1;
        }

        # figure out what files we're supposed to be creating and linking to.
        $slideFile     = "$curr1Index.html";
        $infoFile      = $curr1Index ."_info.html";
        $prevSlideFile = "$prev1Index.html";
        $nextSlideFile = "$next1Index.html";

        # open the slide html file for writing
        open(SLIDE,">$slideFile") or die "Cannot open $slideFile $!\n";

        # open the template file for reading
        open(SLIDETMPL,"<$slideTmpl") or die "Cannot open $slideTmpl: $!\n";

        # read each line of the template file and do what's appropriate
        # for each tag found
        while (<SLIDETMPL>) {
            $line = $_;

            if ($line =~ /ORIG-IMAGE-NAME/) {
                $line =~ s/ORIG-IMAGE-NAME/$albumInfo->{images}[$curr0Index]->{file}/g;

            }
            if ($line =~ /NEXT-SLIDE-NAME/) {
                $tmpVar = $albumInfo->{images}[$next0Index]->{slide};
                $tmpVar =~ s/ /\%20/g;
                $line =~ s/NEXT-SLIDE-NAME/$tmpVar/g;

            }
            if ($line =~ /PREV-SLIDE-HTML/) {
                $line =~ s/PREV-SLIDE-HTML/$prevSlideFile/g;

            }
            if ($line =~ /INDEX-HTML/) {
                $line =~ s/INDEX-HTML/$indexFile/g;

            }
            if ($line =~ /INFO-HTML/) {
                $line =~ s/INFO-HTML/$infoFile/g;

            }
            if ($line =~ /NEXT-SLIDE-HTML/) {
                $line =~ s/NEXT-SLIDE-HTML/$nextSlideFile/g;

            }
            if ($line =~ /SLIDE-IMAGE/) {
                # get some info about the slide
                $origImg  = $albumInfo->{images}[$curr0Index]->{file};
                $origImg  =~ s/ /\%20/g; # make any spaces web friendly
                $slideImg = $albumInfo->{images}[$curr0Index]->{slide};
                $slideImg =~ s/ /\%20/g; # make any spaces web friendly
                $slideX   = $albumInfo->{images}[$curr0Index]->{slidex};
                $slideY   = $albumInfo->{images}[$curr0Index]->{slidey};

                # check to see if we need to link the slide to the original
                if ($opts{lo}) {
                    $tmpVar = "<a href=\"$origImg\"><img src=\"$slideImg\" width=$slideX height=$slideY border=0></a>";
                } else {
                    $tmpVar = "<img src=\"$slideImg\" width=$slideX height=$slideY border=0>";

                }
                $line =~ s/SLIDE-IMAGE/$tmpVar/g;

            }
            if ($line =~ /SLIDE-DESCRIPTION/) {
                $line =~ s/SLIDE-DESCRIPTION/$albumInfo->{images}[$curr0Index]->{desc}/g;

            }
            if ($line =~ /SLIDE-COUNT/) {
                $tmpVar = "\(" . $curr1Index . "\/" . ($#{$albumInfo->{images}} + 1) . "\)";
                $line =~ s/SLIDE-COUNT/$tmpVar/g;
            }

            # print out the line
            print SLIDE $line;
        }

        close SLIDE;   # close the index.html file
        close SLIDETMPL; # close the index_template file
    }
}

###########################################################
# genSlideTemplate - generate the template for each slide page
#
sub genSlideTemplate {

    open(SLIDETMPL,">$slideTmpl") or die "Cannot open $slideTmpl: $!\n";
    
print SLIDETMPL <<endoftemplate;
<html>
<!-- The tag names in this template are used by jGal to insert html code. 
     The tags are in all capitals. They can be moved around in the template
     or deleted if desired. To see how this works just compare the template
     file with a generated slide. -->
<head>
   <title>Image: ORIG-IMAGE-NAME SLIDE-COUNT</title>
   <!---->
   <!-- Preload the next image here -->
   <!---->
   <script language="javascript">       <!--
       if (document.images)    {
          Image1          = new Image();
          Image1.src      = "NEXT-SLIDE-NAME";
       }       //-->
   </script>
</head>

<body bgcolor="#333333" text="#dddddd" link="#95ddff" vlink="#aaaaaa">

<font face="verdana,sans-serif">
<center>
<!---->
<!-- Top Navigation -->
<!---->
<table width=60% border=0 cellspacing=0 cellpadding=0>
    <tr>
        <td align=center><a href="PREV-SLIDE-HTML">&lt;&lt; Prev</a></td>
        <td align=center><a href="INDEX-HTML">Index</a> &nbsp; <a href="INFO-HTML">Info</a></td>
        <td align=center><a href="NEXT-SLIDE-HTML">Next &gt;&gt;</a></td>
    </tr>
</table>

<table cellspacing=0 cellpadding=4>
    <tr><td>&nbsp;</td></tr>
    <tr>
        <!---->
        <!-- Image -->
        <!---->
        <td bgcolor=#ffffff align=center valign=middle>
            SLIDE-IMAGE
        </td>
    </tr>
</table>
<!---->
<!-- Bottom Navigation -->
<!---->
<table width=60% border=0 cellspacing=0 cellpadding=0>
    <!---->
    <!-- Comment -->
    <!---->
    <tr><td>&nbsp;</td></tr>
    <tr>
        <td colspan=3>
            <table width=100% bgcolor="#444444" border=1 cellspacing=0 cellpadding=0>
                <tr><td align=center>SLIDE-DESCRIPTION<br>SLIDE-COUNT</td></tr>
            </table>
        </td>
    </tr>
    <tr><td>&nbsp;</td></tr>
    <tr>
        <td align=center><a href="PREV-SLIDE-HTML">&lt;&lt; Prev</a></td>
        <td align=center><a href="INDEX-HTML">Index</a> &nbsp; <a href="INFO-HTML">Info</a></td>
        <td align=center><a href="NEXT-SLIDE-HTML">Next &gt;&gt;</a></td>
    </tr>
</table>


</font>
</center>
</body>
</html>

endoftemplate

    close SLIDETMPL;
}

###########################################################
# genInfoPages - generate the html EXIF pages
#
sub genInfoPages {
    my (%opts) = %{$_[0]};
    my ($albumInfo) = $_[1];

    my $infoFile      = ""; # file name of info page to create
    my $slideFile     = ""; # slide file associated with current info page
    my $prevSlideFile = ""; # next slide page
    my $prevInfoFile  = ""; # next info page
    my $nextSlideFile = ""; # previous slide page
    my $nextInfoFile  = ""; # previous info page
    my $slideX = "";     # tmp variable to store the slides X dimension
    my $slideY = "";     # tmp variable to store the slides Y dimension
    my $curr0Index = ""; # 0 based index of this image
    my $curr1Index = ""; # 1 based index of this image
    my $prev0Index = ""; # 0 index of previous image
    my $prev1Index = ""; # 1 index of previous image
    my $next0Index = ""; # 0 index of next image
    my $next1Index = ""; # 1 index of next image
    my $tmpVar     = ""; # temp variable
    my $line       = ""; # variable to store current template line in
    
    print "Generating the info html pages\n";

    if (!(-e $infoTmpl)) {
        print "No $infoTmpl file found. Creating\n";
        &genInfoTemplate;
    }

    # create one info page for each image
    for $curr0Index (0 .. $#{$albumInfo->{images}}) {
        # setup all the 0 and 1 based index values
        $curr1Index = $curr0Index + 1;
        $prev0Index = $curr0Index - 1;
        $prev1Index = $curr0Index;
        $next0Index = $curr1Index;
        $next1Index = $curr1Index + 1;

        # handle some special cases
        if ($curr0Index == 0) {
            # this is the first file.
            # the previous index will be the last index
            $prev0Index = $#{$albumInfo->{images}};
            $prev1Index = $#{$albumInfo->{images}} + 1;
        }
        if ($curr0Index == $#{$albumInfo->{images}}) {
            # this is the last file.
            # the next index will be the first index
            $next0Index = 0;
            $next1Index = 1;
        }

        # figure out what files we're supposed to be creating and linking to.
        $infoFile      =  $curr1Index ."_info.html";
        $slideFile     = "$curr1Index.html";
        $prevSlideFile = "$prev1Index.html";
        $prevInfoFile  =  $prev1Index ."_info.html";
        $nextSlideFile = "$next1Index.html";
        $nextInfoFile  =  $next1Index ."_info.html";

        # open the info html file for writing
        open(INFO,">$infoFile") or die "Cannot open $infoFile $!\n";

        # open the template file for reading
        open(INFOTMPL,"<$infoTmpl") or die "Cannot open $infoTmpl: $!\n";

        # read each line of the template file and do what's appropriate
        # for each tag found
        while (<INFOTMPL>) {
            $line = $_;

            if ($line =~ /ORIG-IMAGE-NAME/) {
                $line =~ s/ORIG-IMAGE-NAME/$albumInfo->{images}[$curr0Index]->{file}/g;

            }
            if ($line =~ /NEXT-SLIDE-NAME/) {
                $tmpVar = $albumInfo->{images}[$next0Index]->{slide};
                $tmpVar =~ s/ /\%20/g;
                $line =~ s/NEXT-SLIDE-NAME/$tmpVar/g;

            }
            if ($line =~ /THIS-SLIDE-HTML/) {
                $line =~ s/THIS-SLIDE-HTML/$slideFile/g;

            }
            if ($line =~ /PREV-SLIDE-HTML/) {
                $line =~ s/PREV-SLIDE-HTML/$prevSlideFile/g;

            }
            if ($line =~ /PREV-INFO-HTML/) {
                $line =~ s/PREV-INFO-HTML/$prevInfoFile/g;

            }
            if ($line =~ /INDEX-HTML/) {
                $line =~ s/INDEX-HTML/$indexFile/g;

            }
            if ($line =~ /NEXT-INFO-HTML/) {
                $line =~ s/NEXT-INFO-HTML/$nextInfoFile/g;

            }
            if ($line =~ /NEXT-SLIDE-HTML/) {
                $line =~ s/NEXT-SLIDE-HTML/$nextSlideFile/g;

            }
            if ($line =~ /INFO-IMAGE/) {
                # get the slide dimensions and divide by 2
                $slideX = ($albumInfo->{images}[$curr0Index]->{slidex}/2); 
                $slideY = ($albumInfo->{images}[$curr0Index]->{slidey}/2);
                # get some other info about the slide and image
                $origImg  = $albumInfo->{images}[$curr0Index]->{file};
                $origImg  =~ s/ /\%20/g; # make any spaces web friendly
                $slideImg = $albumInfo->{images}[$curr0Index]->{slide};
                $slideImg =~ s/ /\%20/g; # make any spaces web friendly


                # check to see if we need to link the slide to the original
                if ($opts{lo}) {
                    $tmpVar = "<a href=\"$origImg\"><img src=\"$slideImg\" width=$slideX height=$slideY border=0></a>";
                } else {
                    $tmpVar = "<img src=\"$slideImg\" width=$slideX height=$slideY border=0>";

                }
                $line =~ s/INFO-IMAGE/$tmpVar/g;

            }
            if ($line =~ /SLIDE-DESCRIPTION/) {
                $line =~ s/SLIDE-DESCRIPTION/$albumInfo->{images}[$curr0Index]->{desc}/g;

            }
            if ($line =~ /SLIDE-COUNT/) {
                $tmpVar = "\(" . $curr1Index . "\/" . ($#{$albumInfo->{images}} + 1) . "\)";
                $line =~ s/SLIDE-COUNT/$tmpVar/g;

            }
            if ($line =~ /EXIF-INFO/) {
                # do we have exifInfo to print
                if ($#{$albumInfo->{images}[$curr0Index]->{exif}} > 0) {
                    $tmpVar = ""; # clear out the var
                    # create the exif name column
                    $tmpVar = $tmpVar . "<td>\n";
                    for $j (0 .. $#{$albumInfo->{images}[$curr0Index]->{exif}}) {
                       $tmpVar = $tmpVar . "<nobr>$albumInfo->{images}[$curr0Index]->{exif}->[$j]{field}<br>\n";
                    }
                    $tmpVar = $tmpVar . "</td>\n";

                    # create the exif value column
                    $tmpVar = $tmpVar . "<td>\n";
                    for $j (0 .. $#{$albumInfo->{images}[$curr0Index]->{exif}}) {
                       $tmpVar = $tmpVar . "<nobr>&nbsp;:&nbsp;$albumInfo->{images}[$curr0Index]->{exif}->[$j]{val}<br>\n";
                    }
                    $tmpVar = $tmpVar . "</td>\n";
                } else {
                    $tmpVar = "<td><nobr>No EXIF info available</td>\n";
                }
                $line =~ s/EXIF-INFO/$tmpVar/g;
            }
            # print out the exif info where every line is a full table.
            # not sure why I have this here. I'll probably be removed
            if ($line =~ /EXIF-INFO-FULL-TABLE/) {
                # do we have exifInfo to print
                if ($#{$albumInfo->{images}[$curr0Index]->{exif}} > 0) {
                    $tmpVar = ""; # clear out the var
                    for $j (0 .. $#{$albumInfo->{images}[$curr0Index]->{exif}}) {
                       $tmpVar = $tmpVar . "                            <tr>\n";
                       $tmpVar = $tmpVar . "                                <td valign=top><nobr>$albumInfo->{images}[$curr0Index]->{exif}->[$j]{field}</td>\n";
                       $tmpVar = $tmpVar . "                                <td valign=top><nobr>&nbsp;:&nbsp;</td>\n";
                       $tmpVar = $tmpVar . "                                <td valign=top><nobr>$albumInfo->{images}[$curr0Index]->{exif}->[$j]{val}</td>\n";
                       $tmpVar = $tmpVar . "                            </tr>\n";
                    }
                } else {
                    $tmpVar = "<tr><td>No EXIF info available</td></tr>\n";
                }
                $line =~ s/EXIF-INFO-FULL-TABLE/$tmpVar/g;
            }

            # print the line
            print INFO $line;
        }

        close INFO;   # close the index.html file
        close INFOTMPL; # close the index_template file
    }
}

###########################################################
# genInfoTemplate - generate the template for each info page
#
sub genInfoTemplate {

    open(INFOTMPL,">$infoTmpl") or die "Cannot open $infoTmpl: $!\n";
    
print INFOTMPL <<endoftemplate;
<html>
<!-- The tag names in this template are used by jGal to insert html code. 
     The tags are in all capitals. They can be moved around in the template
     or deleted if desired. To see how this works just compare the template
     file with a generated info page. -->
<head>
   <title>Info for image: ORIG-IMAGE-NAME SLIDE-COUNT</title>
   <!---->
   <!-- Preload the next image here -->
   <!---->
   <script language="javascript">       <!--
       if (document.images)    {
          Image1          = new Image();
          Image1.src      = "NEXT-SLIDE-NAME";
       }       //-->
   </script>
</head>

<body bgcolor="#333333" text="#dddddd" link="#95ddff" vlink="#aaaaaa">

<font face="verdana,sans-serif">
<center>

<!---->
<!-- Top Navigation -->
<!---->
<table width=60% border=0 cellspacing=0 cellpadding=2>
    <tr>
        <td align=left><a href="PREV-SLIDE-HTML">&lt;&lt; Prev</a></td>
        <td align=left><a href="PREV-INFO-HTML">&lt;&lt; Prev Info</a></td>
        <td>&nbsp;</td>
        <td align=right><a href="NEXT-INFO-HTML">Next Info &gt;&gt;</a></td>
        <td align=right><a href="NEXT-SLIDE-HTML">Next &gt;&gt;</a></td>
    </tr>
    <tr>
        <td colspan=5 align=center><a href="INDEX-HTML">Index</a> &nbsp; <a href="THIS-SLIDE-HTML">This Slide</a></td>
    </tr>
</table>

<!---->
<!-- Image/exif info -->
<!---->
<table border=0 cellspacing=0 cellpadding=4>
    <tr><td>&nbsp;</td></tr>
    <tr>
        <!---->
        <!-- Image -->
        <!---->
        <td align=center valign=center>
            <table cellspacing=0 cellpadding=2>
                <tr><td bgcolor="#ffffff" align=center valign=middle>
                    INFO-IMAGE
                </td></tr>
            </table>
        </td>
        <!---->
        <!-- EXIF Info -->
        <!---->
        <td align=left valign=top>
            <table bgcolor="#444466" cellspacing=0 cellpadding=5 border=1>
                <tr>
                    <td>
                        <table border=0 cellspacing=0 cellpadding=0>
                            <tr>
EXIF-INFO
                            </tr>
                        </table>
                    </td>
                </tr>
            </table>
        </td>
        <td>&nbsp;</td>
    </tr>
</table>
<!---->
<!-- Bottom Navigation -->
<!---->
<table width=60% border=0 cellspacing=0 cellpadding=2>
    <!---->
    <!-- Comment -->
    <!---->
    <tr><td>&nbsp;</td></tr>
    <tr>
        <td colspan=5>
            <table width=100% bgcolor="#444444" border=1 cellspacing=0 cellpadding=0>
                <tr><td align=center>SLIDE-DESCRIPTION<br>SLIDE-COUNT</td></tr>
            </table>
        </td>
    </tr>
    <tr><td>&nbsp;</td></tr>
    <tr>
        <td colspan=5 align=center><a href="INDEX-HTML">Index</a> &nbsp; <a href="THIS-SLIDE-HTML">This Slide</a></td>
    </tr>
    <tr>
        <td align=left><a href="PREV-SLIDE-HTML">&lt;&lt; Prev</a></td>
        <td align=left><a href="PREV-INFO-HTML">&lt;&lt; Prev Info</a></td>
        <td>&nbsp;</td>
        <td align=right><a href="NEXT-INFO-HTML">Next Info &gt;&gt;</a></td>
        <td align=right><a href="NEXT-SLIDE-HTML">Next &gt;&gt;</a></td>
    </tr>
</table>

</font>
</center>
</body>
</html>

endoftemplate

    close INFOTMPL;
}
