#!/usr/bin/perl

use warnings;
use strict;

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

my $VERSION = "0.9";
my $brinance_module_version = Brinance::version();
my $about = <<EOD;

This software released under the terms of the GNU General Public License 2.0

Copyright (C) 2004 Jonathan Finger jonathan_finger\@hotmail.com

   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_tk - simple UNIX GUI interface to Brinance Perl personal
           finance planner tracker

Brinance module version: $brinance_module_version
brinance_tk version: $VERSION
EOD

use date_arithmetic qw(validate_date convert_date_to_number convert_number_to_date);
use message_info qw(create_message_window destroy_message_window);
use Tk;
use Tk::ROText;
use Tk::Adjuster;

my $mw = MainWindow->new();
my $font_size = 12;
my $fixed_font = $mw->fontCreate(-family => 'courier', -size => $font_size);
my $proportional_font = $mw->fontCreate(-family => 'times', -size => $font_size);
$mw->title("Brinance");
$mw->configure(-menu => my $menubar = $mw->Menu);
my $menu = $menubar->cascade(-label => '~Menu', -font => $proportional_font);
$menu->command(-label => 'Set Font Size', -command => \&set_font_size,
	       -font => $proportional_font);
$menubar->command(-label => '~About', 
		  -command => \&show_info,
		  -font => $proportional_font);

my $f = $mw->Frame->pack(-side => 'top', -fill => 'x');
my $f2 = $f->Frame->pack(-side => 'top', -fill => 'both');
my $f3 = $f->Frame->pack(-side => 'top', -fill => 'x');
my $f4 = $mw->Frame->pack(-side => 'top', -expand => 1, -fill => 'both');

my $textbox = $f4->Scrolled("ROText",-exportselection => 1, 
			    -height => 15, -width => 120, 
			    -font => $fixed_font,
			    -background => 'white',
			    -cursor => 'left_ptr',
			    -wrap => 'none');
my $adj1 = $f4->Adjuster(-widget => $f4, -side => 'top');
my $message_textbox = $f4->Scrolled("ROText",-exportselection => 1, 
				 -height => 15, -width => 120, 
                                 -font => $fixed_font,
                                 -background => 'white',
				 -wrap => 'none');
my $adj2 = $f4->Adjuster(-widget => $f4, -side => 'top');


$textbox->pack(-side => 'top', -expand => 1, -fill => 'both');
$adj1->packAfter($textbox, -side => 'top');
$message_textbox->pack(-side => 'top', -expand => 1, -fill => 'both');
$adj2->packAfter($message_textbox, -side => 'top');


foreach my $refARef (["Select Acct", \&select_account],
		     ["Credit", [\&enter_credit_debit, 'credit']],
		     ["Debit", [\&enter_credit_debit, 'debit']],
		     ["New Account", \&new_account],
		     ["Exit", sub { exit }],
		     ["Dated Balance", \&dated_balance],
		     ["Clear Messages", \&clear_messages],
		    )
  {
    $f2->Button(-text => $refARef->[0],
	       -background => 'white',
	       -font => $proportional_font,
	       -command => $refARef->[1])->pack(-side => 'left');
  }

my $account_name;
display_account_name();
$f3->Label(-textvariable => \$account_name,
	   -background => 'white',
	   -font => $proportional_font,
	  )->pack(-side => 'left');

my %message_window_args = (args => [-font => "courier $font_size"]);

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


MainLoop();
exit;

my $find;
my $font_window;

sub set_font_size
{
  if ($font_window && Exists($font_window))
    {
      $font_window->raise();
      return;
    }
  $font_window = MainWindow->new();
  $font_window->title("Change Font Size");
  my $font_listbox = $font_window->Scrolled("Listbox", -selectmode => "single",
					    -font => "times 14")->pack();
  my $size = 6;
  while ($size < 25)
    {
      $font_listbox->insert('end', $size);
      $size++;
    }
  $font_window->Button(-text => "Set",
		       -background => 'white',
		       -font => "times 14",
		       -command => sub {set_font($font_listbox->get($font_listbox->curselection()))})->pack();
  $font_window->Button(-text => "Close",
		  -background => 'white',
		  -font => "times 14",
		  -command => sub { $font_window->destroy() })->pack();
}

sub set_font
{
  $font_size = shift;
  $mw->fontConfigure($fixed_font, -size => $font_size);
  $mw->fontConfigure($proportional_font, -size => $font_size);
  $message_window_args{args}->[1] = "courier $font_size";
  if ($find)
    {
      $find->font_size($font_size);
    }
}


sub show_info
{
  create_message_window($about, \%message_window_args);
}


sub addDate
{
  my ($datetime, $days_to_add) = @_;
  my ($year, $month, $day, $hour, $min) = unpack("A4 A2 A2 A2 A2", $datetime);
  if ($min > 59) { $min = 59 }
  if ($hour > 23) { $hour = 23 }

  ($year, $month, $day) = convert_number_to_date( convert_date_to_number($year, $month, $day) + $days_to_add);
  return sprintf("%4d%2d%2d%2d%2d", $year , $month , $day , $hour , $min);
}


sub show_balance
{
  my $datetimeSRef = shift;
  my $out;
  if ($datetimeSRef && length($$datetimeSRef))
    {
      my $datetime = $$datetimeSRef;
      if ($datetime =~ /^\+(\d+)$/)
	{
	  $out = &Brinance::datedbalance(addDate($Brinance::now, $1));
	}
       elsif ($datetime =~ /^\d{12}$/)
	 {
	   $out = &Brinance::datedbalance($datetime);
	 }
       else
	 {
	   $message_textbox->insert("end", "ERROR: Invalid date format $datetime for dated balance\n");
	   return;
	 }
      $out =  "account" . $Brinance::current_acct . " dated balance: " . sprintf ("%.2f", $out) . "\n";
    }
  else
    {
      $out = "$account_name\nBalance: " . sprintf ("%.2f", &Brinance::balance ());
    }
  create_message_window($out, \%message_window_args);
}

sub enter_credit_debit
{
  my $type = shift;
  my $display;
  if ($type eq 'credit') { $display = "Credit" }
  else { $display = "Debit" }
  destroy_old_display();
  my %data = (TYPE => $type, AMT => "", DATE => "", COMMENT => "");
  $textbox->insert("end", "$display Amount: ");
  my $w = $textbox->Entry(-textvariable => \$data{"AMT"},
			  -width => 20,
			  -background => 'white',
			  -font => $proportional_font);
  $textbox->windowCreate('end', -window => $w);
  $textbox->insert("end", "\nCredit Date: ");
  $w = $textbox->Entry(-textvariable => \$data{"DATE"},
			  -width => 20,
			  -background => 'white',
			  -font => $proportional_font);
  $textbox->windowCreate('end', -window => $w);
  $textbox->insert("end", " (optional)\nComment: ");
  $w = $textbox->Entry(-textvariable => \$data{"COMMENT"},
		       -width => 50,
		       -background => 'white',
		       -font => $proportional_font);
  $textbox->windowCreate('end', -window => $w);
  $textbox->insert("end", "\n");
  foreach my $refARef (["Enter $display", [\&enter_credit_debit_ex, \%data]],
		       ["Cancel", \&destroy_old_display],
		       ["Clear Entries", [sub { my $ref = shift; 
						$ref->{"AMT"} = "";
						$ref->{"DATE"} = "";
						$ref->{"COMMENT"}->delete("1.0", "end");
					      }, \%data]])
    {
      $w = $textbox->Button(-text => $refARef->[0], -command => $refARef->[1],
			    -background => 'white',
			    -font => $proportional_font);
      $textbox->windowCreate('end', -window => $w);
    }
}

sub enter_credit_debit_ex
{
  my $refHRef = shift;
  my $amount = $refHRef->{AMT};
  my $datetime = $refHRef->{DATE};
  my $comment  = $refHRef->{COMMENT};
  my $type = $refHRef->{TYPE};
  my $error = "";
  if (!length($amount)) { $error .= "No Amount Entered\n" }
  elsif ($amount !~ /^\d*(\.\d*)?$/) { $error .= "Amount $amount is not numeric\n" }
  if ($datetime =~ /^\+(\d+)$/ ) { $datetime = addDate($Brinance::now, $1) }
  if (length($datetime) && ($datetime !~ /^(\d{12})$/) ) { $error .= "Invalid Datetime $refHRef->{DATE}\n" }
  if ($error)
    {
      create_message_window($error, \%message_window_args);
      return;
    }
  if ( $type eq 'credit' ) { $amount = 1 * $amount }
  else { $amount = -1 * $amount }
  my $out;
  if ($datetime) { $out = Brinance::datedtrans ($datetime, $amount, $comment) }
  else { $out = &Brinance::trans($amount, $comment) }
  if ( 0 == $out )
    {
      my $text = "";
      if (!$datetime || ($datetime <= $Brinance::now))
	{
	  $text = sprintf("Balance after %s %.2f", $type, &Brinance::balance ());
	}
      else
	{
	  $text = "Balance at date $datetime: " . sprintf ("%.2f", Brinance::datedbalance($datetime));
	}
      $message_textbox->insert("end", "$text\n");
      $refHRef->{AMT} = "";
      $refHRef->{DATE} = "";
      $refHRef->{COMMENT} = "";
    }
  else
    {
      my $sub_called;
      if ($datetime) { $sub_called = "Brinance::trans()"}
      else { $sub_called = "Brinance::datedtrans()"}
      if ( -1 == $out )
	{
	  # too few args
	  $message_textbox->insert("end", "ERROR: too few arguments to $sub_called");
	} 
      else
	{
	  $message_textbox->insert("end", "ERROR: undefined output from $sub_called; Check your account files..");
	}
    }
}

sub new_account
{
  destroy_old_display();
  my %new_account_data;
  $textbox->insert("end", "Account #: ");
  my $w = $textbox->Entry(-textvariable => \$new_account_data{"ACCT_#"},
			  -width => 20,
			  -background => 'white',
			  -font => $proportional_font);
  $textbox->windowCreate('end', -window => $w);
  $textbox->insert("end", "\nDescription: ");
  $w = $textbox->Entry(-textvariable => \$new_account_data{"DESC"},
		       -width => 50,
		       -background => 'white',
		       -font => $proportional_font);
  $textbox->windowCreate('end', -window => $w);
  $textbox->insert("end", "\n");
  foreach my $refARef (["Create New Account", [\&enter_new_account, \%new_account_data]], 
		       ["Cancel", \&destroy_old_display],
		       ["Clear Entries", [sub { my $ref = shift; 
						$ref->{"DESC"} = "";
						$ref->{"ACCT_#"} = "";
					      }, \%new_account_data]])
    {
      $w = $textbox->Button(-text => $refARef->[0], -command => $refARef->[1],
			    -background => 'white',
			    -font => $proportional_font);
      $textbox->windowCreate('end', -window => $w);
    }
}

sub enter_new_account
{
  my $refHRef = shift;
  my $acct_num = $refHRef->{"ACCT_#"};
  my $description  = $refHRef->{"DESC"};
  if (length($description) && ($acct_num =~ /^\d+$/))
    {
      my $out = &Brinance::create($description, $acct_num);
      if (1 == $out)
	{
          $message_textbox->insert("end", "Account #: $acct_num already exists\n");
	}
     elsif ( 0 == $out )
       {
	 $message_textbox->insert("end", "Account #: $acct_num created sucessfull\nDesc: $description\n");
	 $refHRef->{"ACCT_#"} = "";
	 $refHRef->{"DESC"} = "";

       }
    else
      {
        $message_textbox->insert("end", "ERROR: undefined output from Brinance::getName(); Check your account files..\n");
      }
    }
  else
    {
      my $errors = "";
      if (!length($description)) { $errors .= "Missing Description\n" }
      if (!length($acct_num)) { $errors .= "Missing Account Number\n" }
      elsif ($acct_num !~ /^\d+$/) { $errors .= "Account Number must be Numeric\n" }
      $message_textbox->insert("end", $errors);
    }
}


sub destroy_old_display
{
  $textbox->delete("1.0", "end");
}


sub dated_balance
{
  destroy_old_display();
  my $datetime = "";
  $textbox->insert("end", "DateTime format YYYYMMDDHHMM or +# for # day in future\n");
  $textbox->insert("end", "DateTime: ");
  my $w = $textbox->Entry(-textvariable => \$datetime,
			  -width => 20,
			  -background => 'white',
			  -font => $proportional_font);
  $textbox->windowCreate('end', -window => $w);
  $textbox->insert("end", "  (optional)\n");
  foreach my $refARef (["Show Dated Balance", [\&show_balance, \$datetime]],
		       ["Cancel", \&destroy_old_display])
    {
      $w = $textbox->Button(-text => $refARef->[0], -command => $refARef->[1],
			    -background => 'white',
			    -font => $proportional_font);
      $textbox->windowCreate('end', -window => $w);
    }
}

sub display_account_name
{
  my $name = Brinance::getName();
  if ($name) { $account_name =  "Current Acct: $Brinance::current_acct name: $name" }
  else { $account_name = "Current Acct: $Brinance::current_acct ** NO NAME **" }
}


sub select_account
{
  destroy_old_display();
  my %account_data;
  $textbox->insert("end", "Select Account #: ");
  my $w = $textbox->Entry(-textvariable => \$account_data{"ACCT_#"},
			  -width => 20,
			  -background => 'white',
			  -font => $proportional_font);
  $textbox->windowCreate('end', -window => $w);
  $textbox->insert("end", "\nSearch by Name: ");
  $w = $textbox->Entry(-textvariable => \$account_data{"DESC"},
		       -width => 50,
		       -background => 'white',
		       -font => $proportional_font);
  $textbox->windowCreate('end', -window => $w);
  $textbox->insert("end", "\n");
  foreach my $refARef (["Select by Acct #", [\&select_account_ex, "select", \%account_data]],
		       ["Search", [\&select_account_ex, "search", \%account_data]],
		       ["Cancel", \&destroy_old_display])
    {
      $w = $textbox->Button(-text => $refARef->[0], -command => $refARef->[1],
			    -background => 'white',
			    -font => $proportional_font);
      $textbox->windowCreate('end', -window => $w);
    }
}

sub select_account_by_number
{
  my $account_num = shift;
  my $errors = "";
  if (!length($account_num)) { $errors .= "No account Number" }
  elsif ($account_num !~ /^\d+$/) { $errors .= "Account Number must be Numeric\n" }
  if ($errors)
    {
      create_message_window($errors, \%message_window_args);
      return;
    }

  my $out = Brinance::switch_acct($account_num + 0);
  my $message = "";
  if ( 1 == $out )
    {
      $message .= "Created account0 (this shouldn't happen here..)\n";
      Brinance::update_future();
    }
  elsif ( 0 == $out )
    {
      $message .= "Now working with account$Brinance::current_acct\n";
      Brinance::update_future();
    } 
  elsif ( -1 == $out )
    {
      $message .= "ERROR: account$Brinance::current_acct doesn't exist.\n";
    } 
  else
    {
      $message .= "ERROR: &Brinance::switch_acct undefined output. Check your data.";
    }
  $message_textbox->insert("end", $message);
  display_account_name();
}

sub select_account_ex
{
  my ($type, $href) = @_;
  if ($type eq 'select')
    {
      select_account_by_number($href->{'ACCT_#'});
    }
  elsif ($type eq 'search')
    {
      my $desc = lc($href->{DESC});
      my @accounts;
      my $dir = $Brinance::account_dir;
      if (!opendir(DIR, $dir))
	{
          $message_textbox->insert("end", "Error trying to read directory: $dir\n");
          return;
	}
      my $name;
      my $errors = "";
      while (defined($name = readdir(DIR)))
	{
          if ($name =~ /^account(\d)+$/)
	    {
              my $acct_num = $1;
              if (!open(INPUT, "<$dir/$name")) { $errors .= "Unable to open $dir/$acct_num\n" }
              elsif (<INPUT> =~ /^\#NAME: (.*)$/) 
                 {
                   if (!length($desc) || (index($1, $desc) > -1)) { push @accounts, [$acct_num, $1] }
		 }
              else { $errors .= "Unbable to find name for Acct: $acct_num\n" }
	    }
	}
      if ($errors) { $message_textbox->insert("end", "$errors") }
      @accounts = sort { $a->[0] <=> $b->[0] } @accounts;
      my $win = MainWindow->new();
      my $fixed_font = $win->fontCreate(-family => 'courier', -size => $font_size);
      my $proportional_font = $win->fontCreate(-family => 'times', -size => $font_size);
      $win->title("List of Accounts");
      my $f = $win->Frame->pack(-side => 'top', -fill => 'x', -expand => 1);
      my $wait = 0;
      foreach my $ref (["Close", sub { $wait = 'close' }],
		       ["Select",sub { $wait = 'select'}])
	{
	  $f->Button(-text => $ref->[0],
		     -font => $proportional_font,
		     -command => $ref->[1])->pack(-side => 'left');
	}
      $f = $win->Frame->pack(-side => 'top', -fill => 'x', -expand => 1);
      my $fmt = "%-10s %s";
      $f->Label(-text => sprintf("  $fmt", "ACCT #", "NAME"),
		-background => 'white',
		-font => $fixed_font,
	       )->pack(-side => 'left');
      $fmt .= "\n";
      my $listbox = $win->Scrolled("Listbox", -width => 120,
				   -height => 30, 
				   -selectmode => 'simple',
				   -font => $fixed_font)->pack();
      foreach my $Aref (@accounts)
	{
	  $listbox->insert('end', sprintf($fmt, @$Aref));
	}
      my @records;
      $win->protocol('WM_DELETE_WINDOW', sub { $wait++ });
      $win->waitVariable(\$wait);
      if (($wait eq 'select') && length($listbox->curselection()))
	{
          select_account_by_number($accounts[$listbox->curselection()]->[0]);
          destroy_old_display();
	}
      $win->destroy();
    }
}

sub clear_messages
{
  $message_textbox->delete("1.0", "end");
}
