#!/usr/bin/perl -w

# renames all given files to have numeric names
# numbers files from 1, incrementing by 1 for each
# pads with zeros up to three digits
#
# WARNING/FIXME: 
#   assumes it will be run in same directory as files
#   it may scramble file names/locations otherwise

use warnings;
use strict;
use English;
use Getopt::Long;

use constant compext => '.dng';

# vars
my $count; # number to rename (next) file to
my $pad;   # number of digits to pad to
my $msg;   # error msg constructed on the fly
# file names and file name components
my $base;  # file basename
my $ext;   # file extension
my $was;   # old complete file name
my $new;   # new complete file name
# names of companion files, analogous to $was/$new
my $compwas;
my $compnew;
# program options - booleans
my $debug; # debugging?
my $skipexist; # skip when new names exist, rather than aborting
my $noop;  # no-op, do not actually do the rename
my $verbose; # describe operations as they happen

# first file defaults to one
$count = 1;

# get options (if any)
die ("command line parse error") if not GetOptions(
	'verbose'	=>	\$verbose,
	'v'		=>	\$verbose,
	'c'		=>	\$skipexist,
	'continue'	=>	\$skipexist,
	'n'		=>	\$noop,
	'd'		=>	\$debug,
	's=i'		=>	\$count,
	'start=i'	=>	\$count
	);

# padding determined by number of arguments
# we only pad up to three digits (arbitrary limit for sanity protection)
my $argc = scalar @ARGV;
if    ($argc <= 0)   { die "missing arguments"; }
elsif ($argc >= 100) { $pad = 3; }
elsif ($argc >= 10 ) { $pad = 2; }
else                 { $pad = 1; }
warn ("pad=$pad") if $debug;
# build format string for sprintf use later
$pad = "%0${pad}d";
warn ("pad=$pad") if $debug;

# regexp to capture file basename and extension
# capture 1: basename (including any embedded extensions)
# capture 2: extension (including leading dot)
my $ext_re = qr{
	^	# anchor start of file name
	(	# capture 1 start: basename
	.+	# one-or-more characters
	)	# capture 1 end
	(	# capture 2 start: extension
	\.	# single literal dot for file extension
	[^.]+	# one-or-more non-dot characters
	)	# capture 2 end
	$	# anchor end of file name
}x;

# loop through each file name argument
ARG: for (@ARGV) {

        # save old name from $_ to $was
        $was = $_;
        warn("was=<$was>") if $debug;

        # extract file basename, and extension if any
	if ($was =~ m/$ext_re/) {
		$base = $1;
		$ext = $2;
		warn "ext=<$ext>" if $debug;
	}
	else {
		# RE failed, so just a basename, no extension (no dots)
		# FIXME what if file name has trailing dot?
		$base = $was;
		$ext = ''; # empty string
		warn "no ext" if $debug;
	}

	# construct hypothetical companion file name
	$compwas = $base . compext;
	warn "compwas=<$compwas>" if $debug;
	# clear $compwas unless file actually exists
	if (not -e $compwas) { undef $compwas; }
	warn "compwas exists" if $debug and defined $compwas;

	FINDNAME: while(1) {

		# construct new file name, number plus extension
		# if extension is empty, just uses number
		# we pad the number with leading zeros as needed
		$new = sprintf($pad,$count) . $ext;
		warn "new=<$new>" if $debug;

		# if we have a companion file, construct new name the same way
		if (defined $compwas) {
			$compnew = sprintf($pad,$count) . compext;
			warn "compnew=<$compnew>" if $debug;
		}

		# counter still increments even if:
		# new name already exists
		# $noop is true
		# rename(s) fail
		# counter does NOT incremement if:
		# old and new names were the same
		#   if continue/skip was set, we'll come around and repeat increment then
		$count++;

		# move on to next file if the names are the same
		# (file name already fits target format, don't need to do it again)
		if ($new eq $was) {
			warn("new name is same as old, skipping arg") if $debug;
			next ARG;
		}
		# sanity check on names for companion file (can't happen)
		die("companion file name did not change") if (defined $compwas and ($compnew eq $compwas));

		# check to see if target (new) name already exists
		# we die/skip if either exist, to keep files paired under old names
		# main file
		if ((-l $new) or (-e $new)) {
			$msg = "'$was' -> '$new': Target name exists";
			die($msg) unless $skipexist;
			warn($msg);
			next FINDNAME;
		}
		# companion file
		if ( (defined $compwas) and ((-l $compnew) or (-e $compnew)) ) {
			$msg = "'$compwas' -> '$compnew': Target name exists";
			die($msg) unless $skipexist;
			warn($msg);
			next FINDNAME;
		}

		# found a good name (does not already exist)
		last FINDNAME;

	} # FINDNAME

	# handle options
	# verbosity: print file names just before we rename them
	if ($verbose) {
		print "'$was' -> '$new'";
		print ", '$compwas' -> '$compnew'" if defined $compwas;
		print "\n";
	}
	# skip rename if no-op
	next if $noop;

	# do the renames
	rename ($was, $new) or
		warn("could not rename '$was' to '$new': $!");
	if (defined $compwas) {
		rename ($compwas, $compnew) or
			warn("could not rename '$compwas' to '$compnew': $!");	
	}

}

