#!/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 = 4		(These two lines should line up)
#       tabstop = 4     (These two lines should line up)

use warnings;
use strict;

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

my $VERSION = '4.03';

# We first pull apart all arguments and push them or their equivalent onto @arglist

my @arglist;
my %options = (
	'help'			=> 'h',
	'version'		=> 'v',
	'credit'		=> 'c',
	'debit'			=> 'd',
	'balance'		=> 'b',
	'datedcredit'	=> 'C',
	'dateddebit'	=> 'D',
	'datedbalance'	=> 'B',
	'name'			=> 'n',
	'create'		=> 'r',
	'all'			=> 'a',
	'datedall'		=> 'A',
	'transfer'		=> 't',
	'datedtransfer'	=> 'T',
);
my $validoptions = join '', (values %options);

foreach (@ARGV) {
	if (/^--./) { # GNU style option
		my $gnu_option = substr $_, 2;

		if (defined $options{$gnu_option}) {
			push @arglist, '-' . $options{$gnu_option};
		} else {
			print STDERR "ERROR: Unrecognized GNU style option: '$gnu_option'\n";
			&usage(1);
		}
	} elsif (/^-./) {
		my @opts = split //, (substr $_, 1);

		my $num = undef;
		foreach my $opt (@opts) {
			if ($opt =~ /[$validoptions]/o) {
				if (defined $num) {
					push @arglist, "-$num";
					$num = undef;
				}
				push @arglist, "-$opt";
			} elsif ($opt =~ /\d/) {
				if (defined $num) {
					$num .= $opt;
				} else {
					$num = $opt;
				}
			} else {
				print STDERR "ERROR: Unrecognized option: '-$opt'\n";
				&usage(1);
			}
		}

		if (defined $num) {
			push @arglist, "-$num";
		}
	} else {
		push @arglist, $_;
	}
}

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

# 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 "$account_dir/accounts") and -e "$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);
}

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

# Process @arglist
while (@arglist) {
	local $_ = shift @arglist;

	if (/^-(\d+)$/) {
		my $out = &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 $current_acct\n";
		} elsif (-1 == $out) {
			print STDERR "ERROR: account $current_acct doesn't exist.\nUse -r or --create\n";
			&usage (1);
		} else {
			print STDERR "ERROR: &switch_acct undefined output. Check your data. Exitting..\n";
			exit (1);
		}
	} elsif ($_ eq '-v') {
		print "Brinance module version: " . &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, 1);
	} elsif ($_ eq '-b') {
		printf ("Balance: %.2f\n", &balance ());
	} elsif ($_ eq '-c') {
		my $amount = shift @arglist;
		my $comment = shift @arglist;
		&CLItrans ($amount, $comment, 1);
	} elsif ($_ eq '-d') {
		my $amount = shift @arglist;
		my $comment = shift @arglist;
		&CLItrans ($amount, $comment, 0);
	} elsif ($_ eq '-C') {
		my $date = shift @arglist;
		my $amount = shift @arglist;
		my $comment = shift @arglist;
		&CLItrans ($amount, $comment, 1, $date);
	} elsif ($_ eq '-D') {
		my $date = shift @arglist;
		my $amount = shift @arglist;
		my $comment = shift @arglist;
		&CLItrans ($amount, $comment, 0, $date);
	} elsif ($_ eq '-B') {
		my $date = shift @arglist;

		if ($date) {
			if ($date =~ /^\d{2,12}$/ or $date =~ /^\+(\d+)$/) {
				$date = &handleDate($date);
			} else {
				print STDERR "ERROR: date not in 12-digit or +<days> format\n";
				&usage (1);
			}

			printf "account $current_acct balance at $date: %.2f\n", &balance($date);
		} else {
			print STDERR "ERROR: dated balance requires an argument\n";
			&usage (1);
		}
	} elsif ($_ eq '-n') {
		if (my $name = &getName()) {
			print "account $current_acct name: $name\n";
		} else {
			print "No name for account $current_acct\n";
		}
	} elsif ($_ eq '-r') {
		my $account = shift @arglist;
		my $name = shift @arglist;

		if ($name && defined $account) {
			my $out = &create ($name, $account);

			if (1 == $out) {
				# already exists
				print "account $account already exists\n";
				exit (0);
			} elsif (0 == $out) {
				# success
				print "account $account created successfully\n";
			} elsif (-1 == $out) {
				# too few
				print "too few arguments to create()\n";
			} else {
				print STDERR "ERROR: undefined output from getName(); Check your accounts file..\n";
				print "Exitting..\n";
				exit (1);
			}
		} else {
			print STDERR "ERROR: too few arguments to create\n";
			&usage (1);
		}
	} elsif ($_ eq '-a') {
		&printAccountSummary();
	} elsif ($_ eq '-A') {
		my $date = &handleDate(shift @arglist);
		&printAccountSummary($date);
	} elsif ($_ eq '-t') {
		my $amount = shift @arglist;
		my $from = shift @arglist;
		my $to = shift @arglist;
		my $comment = shift @arglist;

		&transfer($now, $amount, $from, $to, $comment);
	} elsif ($_ eq '-T') {
		my $date = shift @arglist;
		my $amount = shift @arglist;
		my $from = shift @arglist;
		my $to = shift @arglist;
		my $comment = shift @arglist;

		&transfer($date, $amount, $from, $to, $comment);
	} else {
		# unrecognized option/argument
		print STDERR "ERROR: Unrecognized option: '$_'\n";
		&usage (1);
	}
}

exit (0);

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

	if ($verbose) {
	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>
  -t --transfer -> transfer an amount from one account to another
    $exec -t <amount> <account number from> <account number to> <comment>
  -T --datedtransfer -> dated transfer
    $exec -T 200306271200 <amount> <account number from> <account number to> <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";
	} else {
		print "For more information, type:\n\t$exec --help\n";
	}

	exit $exit;
}

sub CLItrans {
	my ($amount, $comment, $credit, $date) = @_;

	if (defined $amount && $amount == 0) {
		$amount .= '.00';
	}

	$amount = $credit ? $amount : -1 * $amount;

	my $date_mod;
	if ($amount and $comment) {
		my $out;
		if (defined $date) {
			if ($date =~ /^\d{2,12}$/ or $date =~ /^\+\d+$/ ) {
				$date_mod = &handleDate($date);
			} else {
				print STDERR "ERROR: date not formatted correctly\n";
				&usage(1);
			}

			$out = &trans ($amount, $comment, $date_mod);
		} else {
			$out = &trans ($amount, $comment);
		}

		if (&checkTransOutput($out)) {
			print 'Dated transaction applied: ';

			if (defined $date_mod && $date_mod > $now) {
				printf "Balance at date $date_mod: %.2f\n", &balance($date_mod);
			} else {
				printf "Balance after transaction: %.2f\n", &balance();
			}
		} # else: bad output is handled in above function
	} else {
		print STDERR "ERROR: too few arguments for 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 ($min, $hour, $mday, $mon, $year) = (localtime(time+($1*86400)))[1, 2, 3, 4, 5];

		$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 ($now, 0, 12 - (length $in))) . $in;
	} else {
		print STDERR "ERROR: invalid date format: $in\n";
		&usage (1);
	}

	return $date;
}

=pod
sub printAccountSummary: prints account summary
=cut
sub printAccountSummary {
	my ($date) = @_;
	my $dated;
	if (defined $date) {
		$dated = 1;
	} else {
		$dated = 0;
		$date = $now;
	}

	my $ca = $current_acct;

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

	if ($dated) {
		print "Total at $date: " . sprintf('%.2f', $total) . "\n--\n";
	} else {
		print "Total: " . sprintf('%.2f', $total) . "\n--\n";
	}

	&switch_acct($ca);
}

=pod
sub transfer: transfers amount from one account to another in one action
=cut
sub transfer {
	my ($date, $amount, $from, $to, $comment) = @_;
	my $ca = $current_acct;
	$date = &handleDate($date);

	# check exitence of both accounts before anything else
	if (0 != &switch_acct($to + 0)) {
		print STDERR "ERROR: account #$to doesn't exist\n";
		&usage(1);
	}
	if (0 != &switch_acct($from + 0)) {
		print STDERR "ERROR: account #$from doesn't exist\n";
		&usage(1);
	}

	#apply to from
	my $out = &trans (-1 * $amount, "transfer to account #$to: $comment", $date);
	if (&checkTransOutput($out)) {
		printf "Transaction applied to account #$from: Balance at date $date: %.2f\n", &balance($date);
	} # else: bad output is handled in above function

	#apply to to
	&switch_acct($to + 0);
	$out = &trans ($amount, "transfer from account #$from: $comment", $date);
	if (&checkTransOutput($out)) {
		printf "Transaction applied to account #$to: Balance at date $date: %.2f\n", &balance($date);
	} # else: bad output is handled in above function

	#switch back
	&switch_acct($ca);
}

sub checkTransOutput {
	my ($out) = @_;

	if (0 == $out) {
		return 1;
	} elsif (-1 == $out) {
		print STDERR "ERROR: too few arguments to 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 trans(); Check your accounts file..\n";
		print STDERR "Exitting..\n";
		exit (1);
	}

	return 0; # shouldn't get here
}
