#!/usr/bin/perl
#
# This software released under the terms of the GNU General Public License 2.0
#
# Copyright (C) 2003-2006 Brian M. Kelly locoburger@gmail.com http://locoburger.org/
#
#    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, version 2.0.
#
#    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, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# brinance - simple UNIX command-line interface to Brinance Perl personal
#            finance planner tracker
#
#		tabstop = 3		(These two lines should line up)
#     tabstop = 3    (These two lines should line up)

use warnings;
use strict;

use lib "$ENV{HOME}/.brinance/lib";
use Brinance;

my $VERSION = '4.00';
my @arglist;

# We first pull apart all arguments and push them or their equivalent onto @arglist
foreach ( @ARGV ) {
	if ( /^--./ ) { # GNU style option
		if	 ( $_ eq '--help' ) {
			push (@arglist, '-h');
		}
		elsif ( $_ eq '--version' ) {
			push (@arglist, '-v');
		}
		elsif ( $_ eq '--credit' ) {
			push (@arglist, '-c');
		}
		elsif ( $_ eq '--debit' ) {
			push (@arglist, '-d');
		}
		elsif ( $_ eq '--balance' ) {
			push (@arglist, '-b');
		}
		elsif ( $_ eq '--datedcredit' ) {
			push (@arglist, '-C');
		}
		elsif ( $_ eq '--dateddebit' ) {
			push (@arglist, '-D');
		}
		elsif ( $_ eq '--datedbalance' ) {
			push (@arglist, '-B');
		}
		elsif ( $_ eq '--name' ) {
			push (@arglist, '-n');
		}
		elsif ( $_ eq '--create' ) {
			push (@arglist, '-r');
		}
		elsif ( $_ eq '--all') {
			push (@arglist, '-a');
		}
		elsif ( $_ eq '--datedall') {
			push (@arglist, '-A');
		}
		else {
			print STDERR "ERROR: Unrecognized GNU style option: '$_'\n";
			&usage (1);
		}
	}
	elsif ( /^-./ ) { # regular group of options
		# break the arg down into individual list elements
		my $argplace = 1;
		my @donelist = ();
		my $num = '';
		while ($argplace < (length $_)) {
			my $curr_char = substr ($_, $argplace++, 1);
			if ($curr_char =~ /\d/) {
				$num .= $curr_char;
			} else {
				if ($num ne '') {
					push ( @donelist, $num );
					$num = '';
				}
				push ( @donelist, $curr_char );
			}
		}

		if ($num ne '') {
			push ( @donelist, $num );
		}

		foreach (@donelist) {
			push ( @arglist, "-$_" );
		}
	} else { # not starting with -
		push ( @arglist, $_ );
	}
}

# check if we need to convert old account# files to an accounts file
#  if so, suggest they run the included conversion script (don't want conv code in here..)
if ( !(-e "$Brinance::account_dir/accounts") and -e "$Brinance::account_dir/account0") {
	print "It looks like you've been running an older version of brinance.\n";
	print "You will need to convert your account files to the new format.\n";

	print "\nRun the 'conv/toV4' script that was included with this brinance package.\n";

	exit (1);
}

unless (@arglist) {
	print STDERR "Need arguments.\nUse --help for more help\n";
	exit (1);
}

&Brinance::switch_acct (0); # sets us up to use account 0

# helper vars for processing args
my $skipnext = 0;
my $where = 0;
my $credit = 0; #Is the transaction a credit? Debit otherwise..

# Process the args
foreach (@arglist) {
	if ( $skipnext ) { $skipnext--; $where++; next; }

	if (/^-(\d+)$/) {
		my $out = &Brinance::switch_acct ($1 + 0);

		if ( 1 == $out ) {
			print STDERR "WARNING: Created account 0 (this shouldn't happen here..)\n";
		} elsif ( 0 == $out ) {
			print "-- working with account $Brinance::current_acct\n";
		} elsif ( -1 == $out ) {
			print STDERR "ERROR: account $Brinance::current_acct doesn't exist.\nUse -r or --create\n";
			&usage (1);
		} else {
			print STDERR "ERROR: &Brinance::switch_acct undefined output. Check your data. Exitting..\n";
			exit (1);
		}
	} elsif ( $_ eq '-v' ) {
		print "Brinance module version: " . &Brinance::version() . "\n";
		print "brinance CLI version: $VERSION\n";
		print "\n";
		print "Copyright (C) 2003-2006 Brian Kelly locoburger\@gmail.com\n";
		print "brinance comes with ABSOLUTELY NO WARRANTY\n";
		print "This is free software, and you are welcome to redistribute it\n";
		print "under certain conditions. See LICENSE for details.\n";
	} elsif ( $_ eq '-h' ) {
		&usage (0);
	} elsif ( $_ eq '-b' ) {
		print sprintf ("Balance: %.2f\n", &Brinance::balance ());
	} elsif ( $_ eq '-c' ) {
		$credit = 1;
		&CLItrans ();
	} elsif ( $_ eq '-d' ) {
		$credit = 0;
		&CLItrans ();
	} elsif ( $_ eq '-C' ) {
		$credit = 1;
		&CLIdatedtrans ();
	} elsif ( $_ eq '-D' ) {
		$credit = 0;
		&CLIdatedtrans ();
	} elsif ( $_ eq '-B' ) {
		if ( $arglist[$where+1] ) {
			my $date;
			if ($arglist[$where+1] =~ /^\d{2,12}$/ or $arglist[$where+1] =~ /^\+(\d+)$/) {
				$date = &handleDate ($arglist[$where+1]);
			} else {
				print STDERR "ERROR: date not in 12-digit or +<days> format\n";
				&usage (1);
			}

			print sprintf ("account $Brinance::current_acct balance at $date: %.2f\n", &Brinance::balance ( $date ));
		} else {
			print STDERR "ERROR: dated balance requires an argument\n";
			&usage (1);
		}
		$skipnext = 1;
	} elsif ( $_ eq '-n' ) {
		if (my $out = &Brinance::getName ()) {
			print "account $Brinance::current_acct name: $out\n";
		}
		else {
			print "No name for account $Brinance::current_acct\n";
		}
	}
	elsif ( $_ eq '-r' ) {
		if ( $arglist[$where+2] && length($arglist[$where+1]) ) {
			my $out = &Brinance::create ( $arglist[$where+2], $arglist[$where+1] );

			if ( 1 == $out ) {
				# already exists
				print "account $arglist[$where+1] already exists\n";
				exit (0);
			} elsif ( 0 == $out ) {
				# success
				print "account $arglist[$where+1] created successfully\n";
			} elsif ( -1 == $out ) {
				# too few
				print "too few arguments to create()\n";
			} else {
				print STDERR "ERROR: undefined output from Brinance::getName(); Check your accounts file..\n";
				print "Exitting..\n";
				exit (1);
			}
			$skipnext = 2;
		} else {
			print STDERR "ERROR: too few arguments to create\n";
			&usage (1);
		}
	} elsif ($_ eq '-a') {
		my $ca = $Brinance::current_acct;

		print "--\n";
		my @accts = &Brinance::get_accts();
		foreach my $acct (@accts) {
			if (0 == &Brinance::switch_acct($acct)) {
				my $bal = sprintf ('%.2f', &Brinance::balance ());
				my $name = &Brinance::getName;
				$name = $name ? $name : '<No Name>';
				print "Account $acct: $name\nBalance: $bal\n--\n";
			}
		}

		&Brinance::switch_acct($ca);
	} elsif ($_ eq '-A') {
		my $date = &handleDate($arglist[$where+1]);
		my $ca = $Brinance::current_acct;

		print "--\n";
		my @accts = &Brinance::get_accts();
		foreach my $acct (@accts) {
			if (0 == &Brinance::switch_acct($acct)) {
				my $bal = sprintf ('%.2f', &Brinance::balance ($date));
				my $name = &Brinance::getName;
				$name = $name ? $name : '<No Name>';
				print "Account $acct: $name\nBalance at $date: $bal\n--\n";
			}
		}

		&Brinance::switch_acct($ca);
		$skipnext = 1;
	} else {
		# unrecognized option/argument
		print STDERR "ERROR: Unrecognized option: '$_'\n";
		&usage (1);
	}
	$where++;
}

exit (0);

=pod
sub usage: shows how to invoke the command-line program
=cut
sub usage () {
	my $exec = 'brinance';

	print "Usage:
	\$ $exec <args>\n
  Accepted args:
     -b --balance -> show current balance
        \$ $exec -b
     -B --datedbalance -> show dated balance
        \$ $exec -B 200306271200
     -c --credit -> apply credit
        \$ $exec -c <amount> <comment>
     -C --datedcredit -> apply dated credit
        \$ $exec -C 200306271200 <amount> <comment>
     -d --debit -> apply debit
        \$ $exec -d <amount> <comment>
     -D --dateddebit -> apply dated debit
        \$ $exec -D 200306271200 <amount> <comment>
     -# -> (as in, a number) all following arguments refer to this account
        \$ $exec -2 <more arguments>
     -n --name -> get the name of the active account
        \$ $exec -4 -n
     -a --all -> show details of all accounts
        \$ $exec -a
     -A --datedall -> show details of all accounts at given date
        \$ $exec -A 200306271200
     -r --create -> create a new account
        \$ $exec -r <account number> <account description>
     -h --help -> help
        \$ $exec -h
     -v --version -> version
        \$ $exec -v\n";

	print "\nSee README for more information.\n";

	exit ($_[0]);
}

sub CLItrans {
	if ( 0 == $arglist[$where+1] ) { $arglist[$where+1] .= '.00'; }

	if ( $arglist[$where+2] && $arglist[$where+1] ) {  
		my $comment = $arglist[$where+2];
		my $amount = (($credit * 2) - 1) * $arglist[$where+1];

		my $out = &Brinance::trans ( $amount, $comment );

		if ( 0 == $out ) {
			my $bal = sprintf ('%.2f', &Brinance::balance ());

			if ( $credit ) {
				print "Balance after credit: $bal\n";
			} else {
				print "Balance after debit: $bal\n";
			}

			$skipnext = 2;
		} elsif ( -1 == $out ) {
			print STDERR "ERROR: too few arguments to Brinance::trans()\n";
			&usage (1);
		} elsif ( -2 == $out ) {
			print STDERR "ERROR: zero-value transaction\n";
			&usage (1);
		} else {
			print STDERR "ERROR: undefined output from Brinance::trans(); Check your accounts file..\n";
			print "Exitting..\n";
			exit (0);
		}
	}  else {  
		print STDERR "ERROR: too few arguments for a transaction\n";
		&usage (1);
	}  
}

sub CLIdatedtrans {
	if ( $arglist[$where+1] && $arglist[$where+1] == 0) { $arglist[$where+1] .= '.00'; }

	if ( $arglist[$where+3] && $arglist[$where+2] && $arglist[$where+1] ) {
		my $comment = $arglist[$where+3];
		my $date_mod;

		if ( $arglist[$where+1] =~ /^(\d{2,12})$/ or $arglist[$where+1] =~ /^\+(\d+)$/ ) {
			$date_mod = &handleDate ($arglist[$where+1]);
		} else { # date is not formatted properly
			print STDERR "ERROR: date not formatted correctly\n";
			&usage (1);
		}

		my $amount = (($credit * 2) - 1) * $arglist[$where+2];

		my $out = &Brinance::trans ( $amount, $comment, $date_mod );

		if ( 0 == $out ) {
			print 'Dated transaction applied: ';

			if ( $date_mod <= $Brinance::now ) {
				print sprintf ("Current balance: %.2f\n", &Brinance::balance ());
			} else {
				print sprintf ("Balance at date $date_mod: %.2f\n", &Brinance::balance($date_mod));
			}

			$skipnext = 3;
		} elsif ( -1 == $out ) {
			print STDERR "ERROR: too few arguments to Brinance::trans()\n";
			&usage (1);
		} elsif ( -2 == $out ) {
			print STDERR "ERROR: zero-value transaction\n";
			&usage (1);
		} elsif ( -3 == $out ) {
			print STDERR "ERROR: invalid date format specified\n";
			&usage (1);
		} else {
			print STDERR "ERROR: undefined output from Brinance::trans(); Check your accounts file..\n";
			print STDERR "Exitting..\n";
			exit (1);
		}
	} else {
		print STDERR "ERROR: too few arguments for a dated transaction\n";
		&usage (1);
	}
}

=pod
sub handleDate: returns a 12-digit date given a +<date>, <12-digit, or 12-digit
=cut
sub handleDate {
	my ($in) = @_;

	my $date;
	if ($in =~ /^\+(\d+)$/) {
		my (undef,$min,$hour,$mday,$mon,$year,undef,undef,undef) = localtime(time+($1*86400));

		$year += 1900;
		$mon += 1;
		if ($mon  < 10) { $mon  = "0" . $mon }
		if ($mday < 10) { $mday = "0" . $mday }
		if ($hour < 10) { $hour = "0" . $hour }
		if ($min  < 10) { $min  = "0" . $min }
		$date = $year . $mon . $mday . $hour . $min;
	} elsif ($in =~ /^\d{12}$/) {
		$date = $in;
	} elsif ($in =~ /^\d{1,11}$/) {
		$date = (substr ($Brinance::now, 0, 12 - (length $in))) . $in;
	} else {
		print STDERR "ERROR: invalid date format: $in\n";
		&usage (1);
	}

	return $date;
}
