#!/home/bert/scripts/perl
# rename Canon camera files to conform to a standard format
use warnings;
use strict;
use File::Find;
use Getopt::Long;

### parameters

our %dflt_tags = ( 'jpg' => 'img', 'avi' => 'mov', thm => 'mov' );
our $DRYRUN  = 0;
our $VERBOSE = 0;

GetOptions('dryrun|n!'    => \$DRYRUN,
	   'verbose|v+'   => \$VERBOSE)
  or die <<EndOfUsage;
usage: $0 [flags]
flags:
    --dryrun        (-n)   Don't actually rename anything (just show).
    --verbose       (-v)   Show more detail about processing.
EndOfUsage

my $ext_re = '\.(' . join('|', keys %dflt_tags) . ')';

### helpers

sub do_rename ( $$ ) {
  my ($old, $new) = @_;
  $DRYRUN  and warn("WOULD rename $File::Find::dir/$old -> $new\n"), return 1;
  $VERBOSE and warn("rename $File::Find::dir/$old -> $new\n");
  rename $old, $new and return 1;
  warn "rename: $!";
  return;
}

### file name canonicalization

sub canon_num ( $ ) {
  my ($num) = @_;

  $num =~ /^ \d{5} $/x and return $num;
  $num =~ /^ \d+ $/x
    and warn("Strange photo number '$num' found"),
      return sprintf "%05d", $num;

  my ($p1, $p2, $n1, $n2) = ($num =~ /^ (\d)(\d{2}) [-_] (\d{2})(\d{2}) $/x)
    or warn("Misformatted photo number '$num' found"), return $num;
  $n1 == $p2 + ($n2 eq '00' ? 1 : 0)
    or warn("Inconsistent photo number '$num' found"), return $num;
  "$p1$n1$n2";
}

sub canonical ( $ ) {
  my ($file) = @_;
  local ($_) = $file;

  my ($xml, $size, $ext) = ('', '');
  s/(\.xml)$//i         and $xml = lc $1;
  s/$ext_re$//i         and $ext = lc $1;
  $ext or return;
  s/(_(?:Sm|Med|Lg))$// and $size = $1;

  my ($num, $tag) = /^ ( \d+ (?: [-_] \d+)? ) (?: [-_] (\w+) )? $/x
    or warn("Unexpected file name '$file' found"), return;

  $num = canon_num $num;
  $tag = ($tag ? lc $tag : $dflt_tags{$ext});
  "${num}_${tag}${size}.${ext}${xml}";
}

### basic file processing

sub process () {
  -f $_ or return;

  my $rename = canonical($_);
  defined $rename or return;

  $rename eq $_ or do_rename $_, $rename;
  return;
}

### do it

@ARGV = ('.') unless @ARGV;
find({ wanted => \&process }, @ARGV);
