#!/usr/bin/perl
#
# Given an input directory and an output directory, trawl through all the
# files in the input directory (recursively), and :-
#	Extract Exif date
#	Create directory in output directory based on Exif date (if not present)
#	Create softlink in output-directory/Exif with same name as original file

use strict;
use warnings;
use Getopt::Std;
use File::Spec;
#   This doesn't get used as much as it should ... if you're not on a Unix type host you may have trouble.
use Image::ExifTool qw(:Public);

our $VERSION = 0.2;
#   A version number.

our $debug = 0;
#   Output debugging information
our @datetags = ('CreateDate', 'DateCreated', 'DateTimeOriginal');
#   Preferred date tags to pull date from in order of priority
our $dumptags = 0;
#   Whether to simply dump all Exif tags (used for determining above amongst other things)
our $force = 0;
#   Whether to force overwrite of image file symlinks in destination directory.

our $inputdir;
our $outputdir;
our $imagecount = 0;

if (!checkargs()) {
  usage();
  exit(1);
}

our $extool = new Image::ExifTool;
$extool->Options(DateFormat => "%Y-%m-%d");

recurse($inputdir);
print "Completed processing $imagecount images\n";


sub usage {
  print <<END
Usage:
    mk-photo-date-dirs [-h] [-f] [-d] [-g] input-directory output-directory

    Recursively trawls through the contents of input-directory, and for
    each file attempts to extract an Exif date (when the photo was taken).

    If one is obtained, the date is "munged" into the format YYYY-MM-DD
    and used to create a directory in the output directory if one is not
    already present.

    A softlink is then created in the new (or existing) directory with the
    same name as the original file. To make it clearer, the intention is 
    to produce a directory tree along the lines of :-

    output-directory/
                     2008-07-19/
                                B84V6412.TIF
                     2009-11-23/
                                B84V9342.TIF
                                B84V9343.TIF

    This is to work around an "issue" with Bibble in that it does not like
    directories with many files it it.

Options

    -h    Gives usage display (this screen!)
    -d    Shows debugging output
    -f    Force overwrite of files.
    -g    "Gets" all EXIF attributes and values from every image file

Version: $main::VERSION


END

}

sub checkargs {
  my %options;

  getopts('hfdg', \%options);

  if (defined($options{'h'})) {
    usage();
    exit(0);
  }
  
  if (defined($options{'d'})) {
    $debug = 1;
  } 

  if (defined($options{'f'})) {
    $force = 1;
  }

  if (defined($options{'g'})) {
    $dumptags = 1;
  }

  if ($#ARGV != 1) {
    # Strictly speaking '-g' only needs one argument, but not bothering to put that extra check in.
    print "Error with arguments: requires two.\n";
    return 0;
  }
  $inputdir = File::Spec->rel2abs($ARGV[0]);
  $outputdir = File::Spec->rel2abs($ARGV[1]);
  if ($debug) {
    print "debug_options: Two paths determined to be:\n\t$inputdir\n\t$outputdir\n";
  }
  if (! -d $inputdir) {
    print "Error with input directory: $inputdir does not appear to exist.\n";
    return 0;
  }
  if (! -d $outputdir) {
    print "Error with output directory: $outputdir does not appear to exist.\n";
    return 0;
  }

  return 1;
}

sub recurse {
  my $dir = pop(@_);
  my $ref;

  if (!-d $dir) {
    die "Error: $dir does not appear to be a directory\n";
  }

  opendir $ref, $dir or die "Cannot open $dir for reading\n";
  while (my $name = readdir($ref)) {
    # Remember to exclude . and .. or things get a little hectic :-\
    if (($name ne ".") && ($name ne "..")) {
      if (-d "$dir/$name") {
	recurse("$dir/$name");
      } elsif (-f "$dir/$name") {
	# It's a file
	dofile("$dir/$name");
      }
    }
  }
}

sub dofile {
  my $fn = pop(@_);
  my $date;
  if ($extool->ExtractInfo($fn)) {
    if ($dumptags) {
      # This is an option that has nothing to do with the functioning of this script, but is kind of
      # useful in special circumstances, so has been left in.
      my @taglist = $extool->GetFoundTags();
      foreach (@taglist) {
	print "$_\n";
      }
    } else {
      # If it's an image and we're not doing something funky ...
      foreach (@datetags) {
	$date = $extool->GetValue("$_");
	if (defined($date)) { last };
      }
      if (defined($date)) {
	# If we've found a date in the image ...
	print "$fn: $date\n" if $debug;
	createlink($fn, $date);
      } else {
	print "debug_file: Cannot find date for file:$fn\n" if $debug;
      }
      $imagecount++;
    }
  } else {
    print "debug_file: Cannot get EXIF info for file:$fn\n" if $debug;
  }
}

sub createlink {
  my $d = pop(@_);
  my $f = pop(@_);
  my @pathbits = File::Spec->splitpath($f);

  if (! -d "$outputdir/$d") {
    # The directory doesn't exist yet so create it!
    print "debug_dir: Date directory $d being created now\n" if $debug;
    mkdir "$outputdir/$d";
  }
  if (($force) && (-f "$outputdir/$d/$pathbits[2]")) {
    # If the force option is on
    print "debug_file: removing existing file: $outputdir/$d/$pathbits[2]\n" if $debug;
    unlink "$outputdir/$d/$pathbits[2]";
  }
  if (! -f "$outputdir/$d/$pathbits[2]") {
    # The file doesn't exist yet, so create the symlink
    print "debug_file: softlink for file:$f doesn't exist in dated sub-directory as yet.\n" if $debug;
    symlink $f, "$outputdir/$d/$pathbits[2]";
  }
}
