#!/usr/bin/perl -w

# ALE wrapper script
# 
# Copyright (C) 2007 David Hilvert
#
#     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 3 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, see <http://www.gnu.org/licenses/>.

chomp ($ale_exec_dir = `dirname $0`);

%defaults = (
	"ALE_BIN" => "$ale_exec_dir/ale-bin",
	"DCRAW" => "dcraw",
	"EXIF_UTILITY" => "exiftool",
	"ALE_COUNT_THREADS" => "0",
	"PAGER" => "/usr/bin/less",
);

%devices = (
	"Canon EOS 5D" => "canon_300d",
	"Canon EOS DIGITAL REBEL" => "canon_300d",
);

for $default (keys %defaults) {
	if (!defined $ENV{$default}) {
		$ENV{$default} = $defaults{$default};
	}
}

#
# Wrap help page requests
#

if (!defined @ARGV || @ARGV == 0 || $ARGV[0] =~ /^--h[a-zA-Z0-9]$/) {
	$ale_bin = $ENV{"ALE_BIN"};

	#
	# Find a pager
	#
	
	undef $pager;
	if (-x "$ENV{PAGER}") {
		$pager = $ENV{"PAGER"};
	} elsif (`which $ENV{"PAGER"}`) {
		$pager = `which $ENV{"PAGER"}`;
	} elsif (`which pager`) {
		$pager = `which pager`;
	} elsif (`which less`) {
		$pager = `which less`;
	} elsif (`which more`) {
		$pager = `which more`;
	} elsif (-x '/usr/bin/less') {
		$pager = '/usr/bin/less';
	} elsif (-x '/bin/more') {
		$pager = '/bin/more';
	}

	#
	# Set pager options
	#
	
	$ENV{"LESS"} = "-X -F";

	#
	# Fetch help page
	#

	if (defined $pager) {
		exec "/bin/bash -c 'exec -a $0 $ale_bin @ARGV | $pager'";
	} else {
		exec "/bin/bash -c 'exec -a $0 $ale_bin @ARGV'";
	}

	exit(0);
}

#
# Perform metadata extraction and conversion
# 

$started_conversion = 0;
undef $global_device;
for ($i = 0; $i < @ARGV; $i++) {
	$arg = $ARGV[$i];

	$i += 10 if $arg =~ /^-?-3dvp$/;
	$i += 10 if $arg =~ /^-?-3dpp$/;
	$i += 2  if $arg =~ /^-?-3dp$/;
	$i += 2  if $arg =~ /^-?-3dv$/;
	$i += 2  if $arg =~ /^-?-ochain$/;
	$i += 3  if $arg =~ /^-?-wm$/;
	$i += 3  if $arg =~ /^-?-wmx$/;
	$i += 1  if $arg =~ /^-?-trans-load$/;
	$i += 1  if $arg =~ /^-?-trans-save$/;

	next if (!-e $arg); 

	if (defined $ENV{"EXIF_UTILITY"} && `which $ENV{"EXIF_UTILITY"}`) {
		if (`$ENV{"EXIF_UTILITY"} $arg | grep '^EV'` =~ /([0-9\.]+)/) {
			$ev = $1;
			if (`$ENV{"EXIF_UTILITY"} $arg | grep '^ISO'` =~ /([0-9\.]+)/) {
				$ev = $ev - log($1 / 100) / log(2);
			}
			@ARGV = (@ARGV[0..$i - 1], "--ev", "$ev", @ARGV[$i..$#ARGV]);
			$i += 2;
		}
	}

	if (defined $ENV{"DCRAW"} && `which $ENV{"DCRAW"}`) {
		$device_info = `$ENV{"DCRAW"} -i $arg 2> /dev/null`;

		next if ($?);

		if (!$started_conversion) {
			print "Extracting frame data";
			$started_conversion = 1;

			#
			# Make a temporary directory for conversion
			#

			use File::Temp qw/ tempdir /;
			
			$tempdir = tempdir("ale.XXXXXXXXX",
			                   CLEANUP => 1,
					   DIR => File::Spec->tmpdir());
		}

		#
		# Get device information
		#

		undef $device;
		foreach $device_string (keys %devices) {
			if ($device_info =~ /$device_string/) {
				$device = $devices{$device_string};
				last;
			}
		}
		if (!defined $device) {
			$device = "canon_300d";
		}

		#
		# Currently, devices must be global.
		#

		if (defined $global_device && $global_device ne $device) {
			die "Multiple device types not yet supported.\n";
		} else {
			$global_device = $device;
		}

		# @ARGV = (@ARGV[0..$i - 1], "--device", "$device", @ARGV[$i..$#ARGV]);
		@ARGV = ("--device", "$device", @ARGV);

		$i += 2;

		#
		# Convert
		#

		$converted_frame = $arg;
		$converted_frame =~ s/(?:\.[^.]*)?$/.pgm/;
		$converted_frame =~ s/.*\///;
		$converted_frame = $tempdir . "/" . $converted_frame;

		`$ENV{"DCRAW"} -t 0 -d -4 -k 0 -r 1 1 1 1 -c $arg > $converted_frame`;

		$ARGV[$i] = $converted_frame;

		#
		# Try to get black level information
		#

		# XXX: The following approach is not obviously correct.

		if (`$ENV{"DCRAW"} -d -v -c $arg 2>&1 1>/dev/null` =~ /black=(\d+)/) {
			$black_unscaled = $1;
			`$ENV{"DCRAW"} -D -4 -c $arg | pgmhist | tail -1` =~ /(\d+)/;
			$maxval_unscaled = $1;
			`pgmhist $converted_frame | tail -1` =~ /(\d+)/;
			$maxval_scaled = $1;
			
			$black_scaled = $black_unscaled
			              * ($maxval_scaled / $maxval_unscaled)
				      / 65535;
			
			@ARGV = (@ARGV[0..$i - 1], "--black", "$black_scaled", @ARGV[$i..$#ARGV]);
			$i += 2;
		}

		print ".";
	}
}

if ($started_conversion) {
	print "\n";
}

@ARGV = ($0, @ARGV);

if ($ENV{"ALE_COUNT_THREADS"} == 0) {
	system { $ENV{"ALE_BIN"} } @ARGV;
} else {
	$| = 1;
	$fork1 = fork();
	if ($fork1 == 0) {
		exec { $ENV{"ALE_BIN"} } @ARGV;
	}

	if (fork() == 0) {
		$status_file = "/proc/$fork1/status";
		while (-r $status_file && `cat $status_file 2> /dev/null | grep Threads` =~ /(\d+)/) {
			if (defined $threads{$1}) {
				$threads{$1}++;
			} else {
				$threads{$1} = 1;
			}
			sleep 1;
		}
		foreach $count (sort keys %threads) {
			print("$count thread(s): $threads{$count} tick(s)\n");
		}
		exit(0);
	}

	while (wait() != -1) {}
}


