package Text::Wigwam::Library::Math;

    use strict;
    use warnings;
    our $VERSION = '0.01';

    use base q/Exporter/;

    use Text::Wigwam::Const qw/ NUM VAR /;

    our %EXPORT_TAGS = (
        all => [
          qw/
            _proto_abs    _abs
            _proto_add    _add
            _proto_dec    _dec
            _proto_decr   _decr
            _proto_div    _div
            _proto_inc    _inc
            _proto_incr   _incr
            _proto_int    _int
            _proto_mod    _mod
            _proto_mult   _mult
            _proto_random _random
            _proto_sub    _sub
          /
        ],
        abs    => [ qw/ _proto_abs    _abs / ],
        add    => [ qw/ _proto_add    _add / ],
        dec    => [ qw/ _proto_dec    _dec / ],
        decr   => [ qw/ _proto_decr   _decr / ],
        div    => [ qw/ _proto_div    _div / ],
        inc    => [ qw/ _proto_inc    _inc / ],
        incr   => [ qw/ _proto_incr   _incr / ],
        int    => [ qw/ _proto_int    _int / ],
        mod    => [ qw/ _proto_mod    _mod / ],
        mult   => [ qw/ _proto_mult   _mult / ],
        random => [ qw/ _proto_random _random / ],
        sub    => [ qw/ _proto_sub    _sub / ],
    );
    our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
    our @EXPORT = qw( );

=head1 Name

Text::Wigwam::Library::Math - A Wigwam directive library.

=head1 Description

This library provides directives which are useful for calculating values.

=head1 Usage

The directives within this library can be imported into specific branches of
the directive tree (which transcends from  C<Text::Wigwam::Directives>) via
plug-ins or modules using one of the following techniques.

 package Text::Wigwam::Directives::some::arbitrary::branch;
 # import everything
 use Text::Wigwam::Library::Math qw/:all/;

 package Text::Wigwam::Directives::another::arbitrary::branch;
 # import selectively
 use Text::Wigwam::Library::Math(
  qw/ :abs :add :dec :decr :div :inc :incr :int :mod :mult :random :sub /
 );

=head1 Directives

=cut

sub _proto_abs { [ NUM ] }
sub _abs { abs( $_[0]->get_arg ) }

=head2 #abs I<num>

Returns the absolute (positive) form of numeric value I<num>.

 [!! #abs -123 /* output: 123 */ !!]

=cut

sub _proto_add { [ NUM, NUM ] }
sub _add { $_[0]->get_arg + $_[0]->get_arg }

=head2 #add I<num1> I<num2>

Returns the sum of numeric values I<num1> and I<num2>.

 [!! #add 1 2 /* output: 3 */ !!]

=cut

sub _proto_dec { _proto_decr(@_) }
sub _dec { _decr(@_); return }

=head2 #dec I<var>

Decrements a variable's value by 1 and returns nothing.

 [!!
  #define var 5
  #dec var
  "var is " var
  /* output:
   var is 4
  */
 !!]

=cut

sub _proto_decr { [ VAR ] }
sub _decr {
    my $var = $_[0]->get_arg;
    return $_[0]->set_value($var, $_[0]->get_value($var)-1);
}

=head2 #decr I<var>

Decrements a variable's value by 1 and returns the new value.

=cut

sub _proto_div { [ NUM, NUM ] }
sub _div { return $_[0]->get_arg / $_[0]->get_arg }

=head2 #div I<num1> I<num2>

Returns the result of I<num1> divided by I<num2>.

 [!! #div 27 9 /* output: 3 */ !!]

The C<#div> directive is susceptible to I<divide by zero> exceptions. If
I<num2> is zero, this will halt global processing of all templates.

=cut

sub _proto_inc { _proto_incr(@_) }
sub _inc { _incr(@_); return; }

=head2 #inc I<var>

Increments the value of I<var> by 1 and returns nothing.

 [!!
  #define var 1
  #inc var
  "var is " var
  /* output:
   var is 2
  */
 !!]

=cut

sub _proto_incr { [ VAR ] }
sub _incr {
    my ($Api) = @_;
    my $var = $Api->get_arg;
    return $Api->set_value($var, ($Api->get_value($var)||0)+1);
}

=head2 #incr I<var>

Increments the value of I<var> and returns the new value.

=cut

sub _proto_int { [ NUM ] }
sub _int { int( $_[0]->get_arg ) }

=head2 #int I<num>

Returns the integer value of I<num>.

 [!! #int 12.99 /* output: 12 */ !!]

=cut

sub _proto_mod { [ NUM, NUM ] }
sub _mod { return $_[0]->get_arg % $_[0]->get_arg }

=head2 #mod I<num1> I<num2>

Returns the modulus of I<num1> and I<num2>.

 [!! #mod 9 7 /* output: 2 */ !!]

If I<num2> is zero, a I<divide by zero> exception will be thrown.

=cut

sub _proto_mult { [ NUM, NUM ] }
sub _mult { $_[0]->get_arg * $_[0]->get_arg }

=head2 #mult I<num1> I<num2>

Returns the product of I<num1> and I<num2>.

 [!! #mult 25 4 /* output: 100 */ !!]

=cut

srand( time ^ $$ );
sub _proto_random { [ NUM, NUM ] }
sub _random {
    my $var1 = $_[0]->get_arg;
    return $var1 + int( rand( 1 + $_[0]->get_arg - $var1 ) );
}

=head2 #random I<num1> I<num2>

Returns a random integer ranging from I<num1> to I<num2>.

 [!!
  #define die1 #random 1 6
  #define die2 #random 1 6
  "You rolled " #add die1 die2 " (" die1 " & " die2 ")"
 !!]

=cut

sub _proto_sub { [ NUM, NUM ] }
sub _sub { $_[0]->get_arg - $_[0]->get_arg }

=head2 #sub I<num1> I<num2>

Returns the resulting value of I<num1> minus I<num2>.

 [!! #sub 27 9 /* output: 18 */ !!]

=cut

1;
