#!/usr/bin/perl
#===========================================================================
#
# LedgerSMB Command-line script host
#
#
# LedgerSMB 
# Small Medium Business Accounting software
# http://www.ledgersmb.org/
# 
#
# Copyright (C) 2006
# This work contains copyrighted information from a number of sources all used
# with permission.  It is released under the GNU General Public License 
# Version 2 or, at your option, any later version.  See COPYRIGHT file for 
# details.
#
# This is a simple wrapper that allows you to write simple scripts with LSMB
# See sample for the file format.
#
# THIS IS EXPERIMENTAL AND THE INTERFACE IS SUBJECT TO CHANGE

use Parse::RecDescent;
use LedgerSMB::User;
use LedgerSMB::Form;
use LedgerSMB::Sysconfig;

$form = new Form;

$syntax = << '_END_SYNTAX_';

	KEY : /\w[a-z0-9_]*/i
	MODSTR : /\w[a-z0-9_:]*/i
	FNKEY : /(?:\w|:|\-\>)+/
	OP       : m([-+*/%])
	NUMBER : /[+-]?\d*\.?\d+/

	ARGS : /(\w[a-z0-9_]*,?\s?)+/i 

	expression : NUMBER OP expression
              { return main::expression(@item) }
              | KEY OP expression
              { return main::expression(@item) }
              | NUMBER
              | KEY

	assign_instruction : KEY "=" expression
		{ main::assignval($item{KEY}, $item{expression}) }

	call_and_assign : /call/i FNKEY '(' ARGS ')' /into/i KEY
		{ main::call_and_assign($item{FNKEY}, $item{ARGSTR}, $item{KEY}) }	

	FUNCTIONCALL : /call/i FNKEY '(' ARGS ')'
		{ main::call($item{FNKEY}, $item{ARGS});}

	for : /for/i KEY
		{ main::push_loop($item{KEY}) }

	done : /^\s*done\s*$/
		{ main::pop_loop() }

	if : /^\s*if/i KEY
		{ main::if_handler($item{KEY}) }

	# IF is terminated by END IF or FI on its own line

	login : /login/i
		{ main::login() }

	module : /module/i MODSTR
		{ main::load_mod($item{MODSTR}) }

	instruction : assign_instruction
		| call_and_assign
		| FUNCTIONCALL
		| for
		| done
		| if
		| login
		| module

	startrule : instruction

_END_SYNTAX_

 $::RD_HINT = 1;
 $::RD_ERRORS = 1; # Make sure the parser dies when it encounters an error
 $::RD_WARN   = 1; # Enable warnings. This will warn on unused rules &c.`
#$::RD_TRACE = 1;
my @loopstack;
my $loopindex;
my $stackref;
my @control_stack;

push @loopstack, $form;

sub assignval {
	my ($key, $value) = @_;
	if ($key =~ /^ENV:/i){
		$ENV{$key} = $value;
	} else {
		%{$loopstack[$#loopstack - 1]}->{$key} = $value;
	}
}

sub expression {
	shift;
	my ($lhs,$op,$rhs) = @_;
	$lhs = $VARIABLE{$lhs} if $lhs=~/[^-+0-9]/;
	return eval "$lhs $op $rhs";
}

sub call {
	my ($call, $argstr) = @_;
	$argstr =~ s/form/\\\%\$form/;
	$argstr =~ s/user/\\\%myconfig/;
	my @args = split /,\s/, $argstr;
	eval "$call($argstr);\n" || print STDERR $@ . "\n";
}

sub call_and_assign {
	my $key = pop;
	$stackref->{key} = call(@_);
}

sub push_loop {
	my $key = shift;
	my $is_hash = 0;
	if (ref($stackref->{$key}) =~ /HASH/){
		$is_hash = 1;
	} elsif (ref ($stackref->{$key}) !~ /ARRAY/){
		print STDERR "Warning:  Must loop through array or hash.";
	}
	push @loopstack, \$stackref->{$key};
	push @controlstack, 
		{ "key" => $key, 
		'index' => 0,
		'linenum' => $#linestack,
		is_hash => $is_hash };
}

sub pop_loop {
	pop @loopstack;
	$stackref = \$loopstack[$#loopstack];
}

sub if_handler {
	my $key = shift;
	if (!$stackref->{$key}){
		$if_count = 1;
	}
}

sub login {
	$myconfig = new LedgerSMB::User 
		"${LedgerSMB::Sysconfig::memberfile}", "$form->{login}";
	$form->db_init($myconfig);
}

sub load_mod {
	my $mod = shift;
	$mod =~ s/::/\//;
	require "$mod.pm";;
}

my $scriptparse = new Parse::RecDescent($syntax);


$loopindex = 0;
my @linestack;

while ($line = <>){
	push @linestack, $line;
	if ($if_count){
		if ($line =~ /^\s*IF\s/){
			++$if_count;
		}
		if ($line =~ /^(\s*FI\s*|\s*END\s+IF\s*)$/){
			--$if_count;
		}
	}
	next if ($if_count);
	$line =~ s/#.*$//; # strip comments
	$scriptparse->startrule($line);
}

delete $form->{password};

for (keys %$form){
	print "$_ = $form->{$_}\n";
}