#!/usr/bin/perl
#
# Christopher B. Browne, cbbrowne@hex.net, chris_browne@sdt.com
# Web: http://www.conline.com/~cbbrowne  SAP Basis Consultant, UNIX Guy
# Windows NT - How to make a 100 MIPS Linux workstation perform like an 8 MHz 
# 286
#
# $Id: txn,v 1.1.1.1 1999/12/18 02:06:11 curt Exp $

$name = shift(@ARGV); $name =~ tr/A-Z/a-z/;
$homedir = "/home/cbbrowne/kwiken/";

# find the *real* file name (with lots of chances to die if it's not a
# particularly valid name)
$datafile = &find_cbb_file($name, $homedir); 

# See if the file is really and truly a CBB data file
&die_if_not_cbb($datafile);

# initial error checking:
if ($#ARGV != 5) {
   &report_bad_args($#ARGV);
   die(-1);
}

($indate, $check, $payee, $cat, $amount, $desc) = @ARGV;

$txndate = &fiddle_with_date($indate);

if ($amount < 0) {
    $credit = sprintf("%.2f", -$amount);
    $debit=0.0;
} else {
    $debit = sprintf("%.2f", $amount);
    $credit=0.0;
}

#$newbal = sprintf("%.2f", $lastbal + $credit - $debit);

# Check the category/categories
$cat = &split_txn($cat, $amount);
if ($cat eq "-1" || $cat eq "") {
    print "Did not exist!\n";
    die -1;
}

$rec = "";

$txn = "$txndate\t$check\t$payee\t$debit\t$credit\t$cat\t$desc\t$rec";

open(OUT, ">>$datafile");
print OUT $txn, "\n";
close(OUT);

print "Added to $datafile\n";
print "$txn\n";
#print "New balance: $newbal\n";

exit 0;

############################################################
############################################################
############################################################

sub split_txn {
    local ($scat, $amount, $total) = @_;
    if (index($scat, "|", 0) != -1) {
	# Split transaction; look for the pieces, see if they add up
	@PIECES=split(/\|/, $scat);
	if (($#PIECES % 2) == 1) {
	    print "Split does not have appropriate number of pieces\n";
	    die -1;
	}
	@SPLIT = ();  # Initialize the result array
	shift(@PIECES);  # First item gets trashed
	while (@PIECES) {
	    $scat = &find_cats(shift(@PIECES));
	    $samount = &remove_commas(shift(@PIECES));
	    push(@SPLIT, $scat);
	    push(@SPLIT, $samount);
	    $total -= $samount;
	    if (substr($scat, 0, 1) eq "[") {
		# This is a transfer inside a split
		$tftxn = "txn '$scat' '$txndate' '$check' 'Funds Transfer (split)' '[$name]' 
$samount '$desc'";
		push(@TFTXNS, $tftxn);
	    }
	}
	if ((($total - $amount) > 0.005) || (($total - $amount) < -0.005)) {
	    printf "Split amounts add up to %.2f; not the same as the total %.2f\n",
	    $total, $amount;
	    die -1;
	} else {
	    # Re-assemble the string using what was determined here
	    $scat = "|".join("|", @SPLIT);
	    while (@TFTXNS) {
		system (pop(@TFTXNS));
	    }
	}
    } else {
	$scat = &find_cats($scat);
    }
    return $scat;
}


sub find_cats {
    local ($category) = @_;
    $category =~ tr/A-Z/a-z/;
    local (@MATCH, $lowkey, $key, $value);
    
    # Search the category list for matches.  If only one is found, then
    # return it as $cat.  If more than one is found, put them in @MATCH.
    # $lowkey is used for the search, so that it's all case insensitive
    
    $categoryfile = $homedir."categories";
    $match = `grep -i "$category" $categoryfile`;
    @MATCH=();
    @FOUND = split(/\n/, $match);
    foreach $line (0..$#FOUND) {
	($key, $value) = split(/\t/, $FOUND[$line]);
	print "[$key] [$value]\n";
    	$lowkey = $key;
	$lowkey =~ tr/A-Z/a-z/;
        push(@MATCH,$key);
    } 
    
    # Now, see if the category is valid...
    if ($#MATCH == -1) {
	print "No matches found for $category!\n";
	return -1;
    } elsif ($#MATCH == 0) {
#	print "Ok - found $category\n";
	return $MATCH[0];
    } else {
	print "Transaction dated [$txndate]  Ref # [$check] to [$payee]\n";
	    printf "Amount: DR %12.2f CR %12.2f Re: %s\n", $debit, $credit, $desc;
	print "\nCategory code [$category] is ambiguous:\n";
	printf " #  Category Name      Long Description\n";
	    printf "----------------------------------------------------------\n";
	foreach $i (0..$#MATCH) {
	    printf "%2d %-20s %s\n", $i, $MATCH[$i], $CATS{$MATCH[$i]};
	}
	print "Pick one: (invalid entry to abort): ";
	$alt=<STDIN>;
	if (($alt > $#MATCH) || ($alt < 0)
	    || ($alt lt 0) || ($alt gt "99")) {
	    print "Invalid value - ABORT!";
	    exit(-1);
	}
	else 
	{
	    return $MATCH[$alt];
	}
    }
}

sub die_if_not_cbb {
    local ($datafile) = @_;
    $head = `head -1 $datafile`;  # Grab the first line of the file
    if ($head =~ /CBB Data File --/) {
	# OK
	# Pre v0.70 - read the last line, and grab the last balance.
	# Post v0.70 - there is no "balance" field in the .cbb file to read.
	# $lasttxn = `tail -1 $datafile`;  # Grab the last line of the file
	# ($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $lastbal) = split(/\t/, $lasttxn);
	# $lastbal = sprintf("%.2f", $lastbal);
       return;
   } else {
       print $head;
       print "Data file $datafile doesn't look like it's a CBB file\n";
       die -1;
   }
}

sub find_cbb_file {
    local ($id, $homedir) = @_;
    local ($datafile) = $homedir.$id;   
    if (!($datafile =~ /\.cbb/) ){
	$datafile .= ".cbb";
    }
    if (-e $datafile) {
     # Ok
    } else {
	print "Could not find file $datafile\n";
	die -1;
    }
    if (!( -w $datafile)) {
	print "You're not allowed to write to $datafile!\n";
	die -1;
    }
   return $datafile;   
}

sub report_bad_args {
    local ($nargs) = @_;
    print "incorrect argument count - [$nargs]\n";
    print "txn  [Source_acct]  [Date]  [Ref#]  [Payee]  [Category]  [Amount]  
[Comment]\n\n";
    print "    Adds a financial transaction to a cbb file\n";
    print "    use '-t' to fill in today's date\n\n";
    print "Example:\n  dantzig[90]> txn cash -t 'n/a' '1st Cdn Place' 'Lunch' 
4.27 ''\n";
}

sub fiddle_with_date {
    local ($indate) = @_;
    local($g,$g,$g,$day,$month,$year,$g,$g,$g)=localtime(time);
    local($todaydate) = sprintf ("%02d%02d%02d", $year, $month+1,
				 $day+1);
    local ($txndate) = $indate;
    if ($txndate eq "-t") {
	$txndate = $todaydate;
    }
    
    # If the date is 2 digits, then the transaction is merely specifying
    # the day within this month.
    if (length($txndate) <= 2) {
	$txndate = substr($todaydate, 0, 4).sprintf("%02d", $txndate);
    }
    
# If the date is 4 digits long, then it's specifying date and month.
# Insert the year (just YY at this point).
    if (length($txndate) == 4) {
	$txndate = substr($todaydate, 0, 2).sprintf("%04d", $txndate);
    }

    
    $century = "19";		# In the year 2000, this will need to change.
    if (length($txndate) != 8) {
	$txndate = $century.$txndate;
    }
    
    local($year, $month, $day) = (substr($txndate, 0, 4), 
				  substr($txndate, 4, 2),
				  substr($txndate, 6, 2));
    
# This really ought to consider the number of days in each month;
# e.g., February 30th never exists.  I haven't bothered.
    
# Now, validate a whack of stuff all at once, and die if everything
# doesn't seem correct.
    if ((length($txndate) != 8) || ($month < 1) || ($month > 12)
	|| ($day < 1) || ($day > 31)) {
	print "Date [$txndate] formatted incorrectly - use YYYYMMDD\n";
	die -1;
    }
    
    return $txndate;
}
