#!/usr/bin/perl
#
# This software released under the terms of the GNU General Public License 2.0
#
# Copyright (C) 2003 Brian M. Kelly locoburger@netscape.net http://www.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;


# Parse agruments, which in turn, call subroutines..
#
#	Accepted args:
#		b - show balance
#			$ brinance -b
#		B - future balance
#			$ brinance -B <12-digit date>
#		c - credit
#			$ brinance -c <amount> <comment>
#		C - future credit
#			$ brinance -C <12-digit date> <amount> <comment>
#		d - debit
#			$ brinance -d <amount> <comment>
#		D - future debit
#			$ brinance -D <12-digit date> <amount> <comment>
#		# - (as in, a number) all following arguments refer to this (numbered) account
#			$ brinance -2 <action>
#		n - get the name of the active account
#			$ brinance -4 -n
#		r - create a new account
#			$ brinance -C <description>
#		h - help
#			$ brinance -h
#		v - version
#			$ brinance -v

my $VERSION = "3.01";
my @arglist;

# We first pull apart all arguments and push them or their equivalent into @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 "--futurecredit" ) {
			push ( @arglist, "C" );
		}
		elsif ( $_ eq "--futuredebit" ) {
			push ( @arglist, "D" );
		}
		elsif ( $_ eq "--futurebalance" ) {
			push ( @arglist, "B" );
		}
		elsif ( $_ eq "--name" ) {
			push ( @arglist, "n" );
		}
		elsif ( $_ eq "--create" ) {
			push ( @arglist, "r" );
		}
		else {
			print "Unrecognized GNU style option: $_\n";
			&usage (1);
		}
	}
	elsif ( /^-/ ) # regular group of options
	{
		my @revlist = (); # reversed list
		my @tmplist = (); # reversed reversed list
		my @donelist = (); # and now with the numbers all bunched together
		my $work = "";
		# This pulls it apart in reverse order
		while ( $_ ne "-" )
		{
			$work = chop $_;
			push ( @revlist, $work );
		}

		# This puts it back in forward order
		my $j = 0;
		for ( my $i = (@revlist-1); $i >= 0; $i-- )
		{
			$tmplist[$j++] = $revlist[$i];
		}

		# This groups the arguments logically.. digits bunched and others seperated
		my $num = "";
		foreach (@tmplist)
		{
			if (/\d/)
			{
				$num .= $_;
			}
			else
			{
				if ($num ne "")
				{
					push ( @donelist, $num );
					$num = "";
				}
				push ( @donelist, $_ );
			}
		}

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

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

&Brinance::switch_acct (0); # sets us up to use account0
&Brinance::update_future (); # sync with future transactions in account0

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

if ( 0 == @arglist )
{
	print "Need arguments.\nUse --help for more help\n";
}

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

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

		if ( 1 == $out ) {
			print "Created account0 (this shouldn't happen here..)\n";
		}
		elsif ( 0 == $out ) {
			print "Now working with account$Brinance::current_acct\n";
		}
		elsif ( -1 == $out ) {
			print "ERROR: account$Brinance::current_acct doesn't exist.\nUse -r or --create\n";
			&usage (1);
		}
		else {
			print "ERROR: &Brinance::switch_acct undefined output. Check your data. Exitting..\n";
			exit (1);
		}

		&Brinance::update_future ();
	}
	elsif ( $_ eq "v" ) {
		my $mod_ver = &Brinance::version ();
		print "Brinance module version: $mod_ver\n";
		print "brinance CLI version: $VERSION\n";
	}
	elsif ( $_ eq "h" ) {
		&usage (0);
	}
	elsif ( $_ eq "b" ) {
		print "Balance: " . sprintf ("%.2f", Brinance::balance ()) . "\n";
	}
	elsif ( $_ eq "c" ) {
		$credit = 1;
		&CLItrans ();
	}
	elsif ( $_ eq "d" ) {
		$credit = 0;
		&CLItrans ();
	}
	elsif ( $_ eq "C" ) {
		$credit = 1;
		&CLIfuturetrans ();
	}
	elsif ( $_ eq "D" ) {
		$credit = 0;
		&CLIfuturetrans ();
	}
	elsif ( $_ eq "B" ) {
		if ( $arglist[$where+1] )
		{
			my $out;

			if ($arglist[$where+1] =~ /^\+(\d+)$/) {
				$out = &Brinance::futurebalance ( &addDate ($Brinance::now, $1) );
			}
			elsif ($arglist[$where+1] =~ /^\d{12}$/) {
				$out = &Brinance::futurebalance ( $arglist[$where+1] );
			}
			else {
				print "ERROR: Invalid date format for future balance\n";
				&usage (1);
			}

			if ( $out ) {
				# success
				print "account" . $Brinance::current_acct . " future balance: " . sprintf ("%.2f", $out) . "\n";
			}
			else {
				print "ERROR: invalid argument to Brinance::futurebalance()\n";
				&usage (1);
			}
		}
		else {
			print "ERROR: future 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] && $arglist[$where+1] )
		{
			my $out = &Brinance::create ( $arglist[$where+1], $arglist[$where+2] );

			if ( 1 == $out )
			{
				# already exists
				print "account$arglist[$where+2] already exists\n";
				exit (0);
			}
			elsif ( 0 == $out )
			{
				# success
				print "account$arglist[$where+2] created successfully\n";
			}
			elsif ( -1 == $out )
			{
				# too few
			}
			else
			{
				print "ERROR: undefined output from Brinance::getName(); Check your account files..\n";
				print "Exitting..\n";
				exit (1);
			}

			$skipnext = 2;
		}
		else
		{
			print "ERROR: too few arguments to create\n";
		}
	}
	else {
		# unrecognized option/argument
		print "Unrecognized option: '$_'\n";
		&usage (1);
	}

	$where++;
}

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

	print "Usage:\n
	\$ $exec <args>\n
  Accepted args:
     -b --balance -> show balance
        \$ $exec -b
     -B --futurebalance -> show future balance
        \$ $exec -B 200306271200
     -c --credit -> credit
        \$ $exec -c <amount> <comment>
     -C --futurecredit -> future credit
        \$ $exec -C 200306271200 <amount> <comment>
     -d --debit -> debit
        \$ $exec -d <amount> <comment>
     -D --futuredebit -> future debit
        \$ $exec -D 200306271200 <amount> <comment>
     -# -> (as in, a number) all following arguments refer to this account
        \$ $exec -2 <action>
     -n --name -> get the name of the active account
        \$ $exec -4 -n
     -r --create -> create a new account
        \$ $exec -r <account description> <account number>
     -h --help -> help
        \$ $exec -h
     -v --version -> version
        \$ $exec -v\n";

	exit ($_[0]);
}

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

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

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

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

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

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

sub CLIfuturetrans {
	if ( $arglist[$where+1] && $arglist[$where+1] == 0) { $arglist[$where+1] .= ".0"; }

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

		if    ( $arglist[$where+1] =~ /^\+(\d+)$/ ) {
			$date_mod = &addDate ( $Brinance::now, $1 );
		}
		elsif ( $arglist[$where+1] =~ /^(\d{12})$/ ) {
			$date_mod = $1;
		}

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

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

		if ( 0 == $out )
		{
			# success
			print "Future transaction applied\n";

			$skipnext = 3;
		}
		elsif ( -1 == $out )
		{
			# too few args
			print "ERROR: too few arguments to Brinance::futuretrans()\n";
			&usage (1);
		}
		elsif ( -2 == $out )
		{
			# zero trans
			print "ERROR: zero-value transaction\n";
			&usage (1);
		}
		elsif ( -3 == $out )
		{
			# invalid date format
			print "ERROR: date specified either invalid or in the past\n";
			&usage (1);
		}
		else
		{
			print "ERROR: undefined output from Brinance::futuretrans(); Check your account files..\n";
			print "Exitting..\n";
			exit (0);
		}
	}
	else
	{
		print "ERROR: too few arguments for a future transaction\n";
		&usage (1);
	}
}

=pod
sub addDate: adds a second argument (number of days) to the first argument (a whole 12-digit date)
	accounts for different length months and leap-year, but not leap-leap years (ie 2200)
=cut
sub addDate (){
	$_ = $_[0];
	my $added = $_[1];

	/(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)/;

	my $year = $1;
	my $month = $2;
	my $day = $3;
	my $hour = $4;
	my $min = $5;

	# now, to do some rounding..

	# Let's keep the minutes and hours simple..
	if ($min > 59) {
		$min = 59;
	}
	if ($hour > 23) {
		$hour = 23;
	}

	# months are crazy go nuts..
	my %months = ( 1 => 31,  2 => 28,  3 => 31,
	               4 => 30,  5 => 31,  6 => 30,
   	            7 => 31,  8 => 31,  9 => 30,
      	        10 => 31, 11 => 30, 12 => 31 );

	# Make sure it's all numeric so the hash works and our comparisons are valid
	$day += ($added * 1); #Oh, and add the days..
	$month *= 1;

	my $leap = 0;

	while ($day > $months{$month}) {
		if ( $month == 2 && (($year % 4) == 0)) {
			if ( 29 == $day )
			{
				$leap = 1;
				$day = 28; # so we don't get stuck in the loop, we'll set it back to 29 later
			}
			else
			{
				$day -= 29;
				$month++;
			}
		}
		else {
			$day -= $months{$month};
			$month++;
		}

		while ($month > 12)
		{
			$month -= 12;
			$year++;
		}
	}

	if ($leap) # we set day to 28 before to pass the loop, but the day is really 29
	{
		$day = 29;
	}

	while ($month > 12)
	{
		$month -= 12;
		$year++;
	}

	if ($year > 9999)
	{
		$year = 9999; # Final sanity check.. or is it?
	}

	$month *= 1; $day *= 1;

	if ($month < 10) {
		$month = "0" . $month;
	}
	if ($day < 10) {
		$day = "0" . $day;
	}

	return $year . $month . $day . $hour . $min;
}

