#!/usr/bin/perl
# Filename:	fix_img, normalize, geometry, etc..
# Author:	David Ljung Madison <DaveSource.com>
# See License:	http://MarginalHacks.com/License/
# Description:	Run ImageMagick 'convert' on a photo, optionally saving a backup
# By itself (as "fix_img") can sometimes repair broken images
#
# Name the executable either "fix_img" or one of converts options
# (such as geometry for -geometry, normalize for -normalize, etc..)
#
# You can also use links to a main executable, for example (on unix):
# % ln -s fix_img normalize
# % ln -s fix_img geometry
# etc..
# 
# The program will need extra arguments if the convert option requires them.
# See examples in usage.
use strict;

##################################################
# Setup the variables
##################################################
my $PROGNAME = $0; $PROGNAME =~ s|.*/||;
my ($BASENAME,$PROGNAME) = ($0 =~ m|(.*)/(.+)|) ? ($1?$1:'/',$2) : ('.',$0);

my @DEFAULT_BAK = qw(normalize);	# I like to do backups by default when norm..

my $CONVERT = "convert";	# Use full path for windows systems
my $VIEWER = "xv";	# Default graphics viewer (with args)

##################################################
# Usage
##################################################
sub fatal {
	foreach my $msg (@_) { print STDERR "[$PROGNAME] ERROR:  $msg\n"; }
	exit(-1);
}

sub usage {
	my ($opt) = shift @_;
	foreach my $msg (@_) { print STDERR "ERROR:  $msg\n"; }

	my $bak = $opt->{bak} ? "ON" : "OFF";
	my $arg = $PROGNAME eq "fix_img" ? "" : " with '-$PROGNAME'";
	print STDERR <<USAGE;

Usage:\t$PROGNAME [-d] <images>
  Run ImageMagick 'convert'$arg on a bunch of images.
  -d              Set debug mode
  -q              Run quiet (no "Done: .." messages)
  -bak            Save backups [default $bak]
  -nobak          Don't save backups
  -view           Run image viewer when done
  -viewer <prog>  Image viewer [default $opt->{viewer}]
	--opts .. --    Options to add to convert

Name the executable either "fix_img" or one of convert's options.
Example runs:

% fix_img *.jpg
% normalize *.jpg
% geometry 50% *.jpg
% geometry 50% -bak -view *.jpg

USAGE
	exit -1;
}

sub convert_usage {
	my ($arg) = @_;
	open(CONV,"$CONVERT -h|") || die("Couldn't read convert usage output\n");
	while (<CONV>) {
		if (/^\s+-($arg.*)/) {
			# Heuristic for determining if convert usage shows an arg.
			# Chop off after 20 characters (that's the usage sentence)
			# And split to see how many args might be mentioned.
			my @usage = split(/\s+/,substr($1,0,20));
			return @usage ? $#usage : 0;	# Number of args needed.  (Likely 0, 1 or 2)
		}
	}
	close CONV;
	die("Unknown convert option [-$arg]\n");
}

sub parse_prog {
	my $opt = {};
	$opt->{args} = [];
	$opt->{viewer} = $VIEWER;

	my $arg = $PROGNAME;
	return $opt if $arg eq "fix_img";

	push(@{$opt->{args}}, "-$arg");

	my $needs_args = convert_usage($arg);

	foreach ( 1..$needs_args ) {
		push(@{$opt->{args}}, shift @ARGV);
	}

	$opt->{bak} = 1 if grep($arg eq $_, @DEFAULT_BAK);

	$opt;
}

sub get_dashdash {
	my @dash;
	while ($#ARGV>=0) {
		my $arg=shift(@ARGV);
		last if $arg eq "--";
		push(@dash,$arg);
	}
	@dash;
}

sub parse_args {
	my $opt = parse_prog();

	while (my $arg=shift(@ARGV)) {
		if ($arg =~ /^-h$/) { usage($opt); }
		if ($arg =~ /^-d$/) { $MAIN::DEBUG=1; next; }
		if ($arg =~ /^-q$/) { $opt->{q}=1; next; }
		if ($arg =~ /^-bak$/) { $opt->{bak}=1; next; }
		if ($arg =~ /^-nobak$/) { $opt->{bak}=0; next; }
		if ($arg =~ /^-view$/) { $opt->{view}=1; next; }
		if ($arg =~ /^-viewer$/) { $opt->{viewer}=shift(@ARGV); next; }
		if ($arg =~ /^--(convert_)?opts$/) { push(@{$opt->{args}},get_dashdash()); next; }
		if ($arg =~ /^-/) { usage($opt,"Unknown option: $arg"); }
		push(@{$opt->{files}},$arg);
	}
	usage($opt,"No files defined") unless $opt->{files};
	
	$opt;
}

sub debug {
	return unless $MAIN::DEBUG;
	foreach my $msg (@_) { print STDERR "[$PROGNAME] $msg\n"; }
}

##################################################
# Main code
##################################################
sub main {
	my $opt = parse_args();
	
	my @view;
	foreach my $file ( @{$opt->{files}} ) {
		my $orig = $file;
		push(@view,$file);
		if ($opt->{bak}) {
			$orig =~ s/(\.(gif|jpg|jpeg))?$/.orig$1/i;
			if (-f $orig) {
				print "Already has original: $orig\n";
				next;
			}
			rename($file,$orig);
			push(@view,$orig);
		}
		system($CONVERT,@{$opt->{args}},$orig,$file);
		print STDERR "ERROR: $file: $?\n" if $?;
		print "Done: $file\n" unless $opt->{q};
	}

	exec($opt->{viewer},@view) if $opt->{view};
}
main();
