#!/usr/bin/perl
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
#NAME
#  mileage - Calculate gas mileage
#
#SYNOPSIS
#  mileage [file]..
#
#REQUIRES
#
#DESCRIPTION
#  This program reads a file full of gas records like:
#    date: 2008-10-11
#    mile: 35685
#    amnt: 10.499 g
#    rate:  $3.139
#    cost: $32.95
#
#  The output is the same lines, with an "mpg: ..." line added whenever it
#  can be calculated.
#
#  Some simple heuristics are used to determine when the calculation can be
#  done.  We use the following values to make the decision:
	$maxmiles = 400;	# Don't believe any miles-driven above this
	$maxmpg   =  40;	# Don't believe any miles-per-gallon above this
#
#OPTIONS
#
#EXAMPLES
#
#FILES
#
#BUGS
#
#SEE ALSO
#
#AUTHOR
#  John Chambers <jc@trillian.mit.edu>
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #

$| = 1;
$exitstat = 0;
($P = $0) =~ s".*/"";
$V = $ENV{"V_$P"} || $ENV{"D_$P"} || 1;	# Verbose level.

%curr = ();		# Current block's data
%prev = ();		# Previous block's data

line:
for $line (<>) {
	$line =~ s/[\r\s]+$//;
	print "#LINE: \"$line\"\n" if $V>2;
	unless ($line) {		# Blank line ends a block of data
		&calc() if %curr && %prev;	# Do the calculations for the block
		%prev = %curr;		# Remember one previous block
		%curr = ();
		print "\n";			# Blank line as block separator
		next line;
	}
	$line =~ s/:\s*/:	/;
	print "$line\n" if $V>0;
	if ($line =~ /^\s*#/) {	# Comments are ignored.
	} elsif ($line =~ /^date:\s*([-\$\d]+)$/) {	# Date probably isn't used
		$curr{date} = $1;
	} elsif ($line =~ /^mile:\s*(\d+)$/) {	# Miles
		$curr{mile} = int($1);
	} elsif ($line =~ /^amnt:\s*([.\d]+)\s*g[alons]*$/) {	# Amount in gallons
		$curr{amnt} = $1;
	} elsif ($line =~ /^rate:\s*\$([.\d]+)$/) {	# $/gallon
		$curr{rate} = $1;
	} elsif ($line =~ /^rate:\s*\$([.\d]+)\/g/) {	# $/gallon
		$curr{rate} = $1;
	} elsif ($line =~ /^cost:\s*\$([.\d]+)$/) {	# $ Total cost
		$curr{cost} = $1;
	} elsif ($line =~ /^(\w+):\s*(.+)$/) {	# $ Anything else
		$curr{$1} = $2;
	} else {
		print "## \"$line\" not recognized.\n" if $V>1;
	}
}
if (%curr) {	# No blank line at end
	&calc() if %curr && %prev;	# Do the calculations for the block
	print "\n";	# Make sure we include final blank line
}

print "$P: Exit with status $exitstat.\n" if $V>1;
exit $exitstat;

sub calc {
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
	local($dc,$dp,$g,$m,$mc,$mp,$mpg);
	if ($mpg = $curr{mpg}) {
		print "## Already calculated $mpg mpg.\n" if $V>1;
		return;
	}
	unless (($mc = $curr{mile}) > 0 && ($mp = $prev{mile}) > 0) {
		print "#Miles bad: prev='$mp' curr='$mc'\n" if $V>2;
		return;
	}
	if ($mc <= $mp) {
		$dc = $curr{date};
		$dp = $prev{date};
		print "\n## Miles $mp, $mc out of order [dates $dp, $dc] \n" if $V>0;
		return;
	}
	$m = $mc - $mp;
	print "#Miles: $m (prev=$mp curr=$mc)\n" if $V>2;
	if ($m > $maxmiles) {
		print "# Gap of $m miles ignored.\n" if $V>0;
		return;
	}
	print "miles:	$m\n" if $V>0 && !$curr{miles};
	unless ($g = $curr{amnt}) {
		print "#Bad gallons '$g'\n" if $V>2;
		return;
	}
	print "#Gallons: $g.\n" if $V>2;
	if ($m > 0 && $g > 0) {
		$mpg = $m/$g;
		if ($mpg > $maxmpg) {
			print "\n## Miles $mpg > $maxmpg max mileage; ignored.\n" if $V>1;
			return;
		}
		printf "mpg:	%3.1f	($m / $g)\n",$mpg;
		$mtot += $m;
		$gtot += $g;
		$mpgt = $mtot / $gtot;
		printf "mpgt:	%3.1f\n",$mpgt;
	}
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
