package Acme::PlayCode::Plugin::NumberPlus;

use Moose::Role;
use List::MoreUtils qw/insert_after/;
use PPI::Token::Comment;

our $VERSION   = '0.11';
our $AUTHORITY = 'cpan:FAYLAND';

around 'do_with_token_flag' => sub {
    my $orig = shift;
    my $self = shift;
    my ( $token_flag ) = @_;
    
    my @tokens = $self->tokens;
    my $token  = $tokens[$token_flag];
    
    use Data::Dumper;
#    print STDERR Dumper(\$token);
    
    my $orginal_flag = $token_flag;
    if ( $token->isa('PPI::Token::Operator') ) {
        my $op = $token->content;
        # only '+' '-' '*' '/' are do-able
        if ( $op eq '+' or $op eq '-' or $op eq '*' or $op eq '/' ) {
            # get next tokens
            my (@next_full_tokens);
            while ( $token_flag++ ) {
                if ($tokens[$token_flag]->isa('PPI::Token::Whitespace') ) {
                    push @next_full_tokens, $tokens[$token_flag];
                    next;
                }
                last if ( $tokens[$token_flag]->isa('PPI::Token::Structure') );
                if ( $tokens[$token_flag]->isa('PPI::Token::Operator') ) {
                    my $op2 = $tokens[$token_flag]->content;
                    unless ( $op2 eq '+' or $op2 eq '-' or $op2 eq '*' or $op2 eq '/' ) {
                        last;
                    }
                }
                last unless ( $tokens[$token_flag] );
                push @next_full_tokens, $tokens[$token_flag];
            }
            # remove last space
            pop @next_full_tokens if ( $next_full_tokens[-1]->isa('PPI::Token::Whitespace'));
            $token_flag = $orginal_flag; # roll back
            # get prev tokens
            my (@prev_full_tokens);
            while ($token_flag--) {
                if ($tokens[$token_flag]->isa('PPI::Token::Whitespace') ) {
                    unshift @prev_full_tokens, $tokens[$token_flag];
                    next;
                }
                last if ($tokens[$token_flag]->isa('PPI::Token::Structure'));
                if ( $tokens[$token_flag]->isa('PPI::Token::Operator') ) {
                    my $op2 = $tokens[$token_flag]->content;
                    unless ( $op2 eq '+' or $op2 eq '-' or $op2 eq '*' or $op2 eq '/' ) {
                        last;
                    }
                }
                last unless ( $tokens[$token_flag] );
                unshift @prev_full_tokens, $tokens[$token_flag];
            }
            $token_flag = $orginal_flag; # roll back
            # remove first space
            shift @prev_full_tokens if ( $prev_full_tokens[0]->isa('PPI::Token::Whitespace'));

            # only do-able for number, space, operator
            my $do_able = 1;
            $do_able = 0 unless (scalar @prev_full_tokens and scalar @next_full_tokens);
            if ( $do_able ) {
				foreach ( @prev_full_tokens, @next_full_tokens ) {
					unless ( $_->isa('PPI::Token::Whitespace') or $_->isa('PPI::Token::Number') or
						( $_->isa('PPI::Token::Operator') and $_->content =~ /^[\+\-\*\/]$/ ) ) {
							$do_able = 0;
							last;
					}
				}
			}
            if ( $do_able ) {
                # remove prev full tokens
                my $prev_num = scalar @prev_full_tokens;
                my $next_num = scalar @next_full_tokens;
                my @output = @{ $self->output };
                @output = splice( @output, 0, scalar @output - $prev_num );
                                
                my $str = join('', @prev_full_tokens, $token, @next_full_tokens);
                $str = eval($str);
                push @output, $str;
                my $comment = " # $str = ";
                foreach ( @prev_full_tokens, $token, @next_full_tokens ) {
                    $comment .= $_->content;
                }

                # move 'token flag' i forward
                $token_flag += $next_num + 1;
                my $to_be_set = $token_flag;
                
                # add comment like ' # 3 = 1 + 2'
                while ( $token_flag ) {
                    my $_token = $tokens[$token_flag];
                    unless ( $_token ) {
                        push @tokens, new PPI::Token::Comment($comment);
                        last;
                    }
                    push @output, $orig->($self, $token_flag);
                    $token_flag++;
                    if ( $_token->isa('PPI::Token::Structure') and
                         $_token->content ne ')' ) {
                        insert_after { $_ eq $_token } new PPI::Token::Comment($comment) => @tokens;
                        last;
                    }
                }

                $self->token_flag( $token_flag );
                $self->output( \@output );
                $self->tokens( \@tokens );
                return;
            }
        }
    }
    
    $orig->($self, @_);
};

no Moose::Role;

1;
__END__

=head1 NAME

Acme::PlayCode::Plugin::NumberPlus - Play code with plus number

=head1 SYNOPSIS

    use Acme::PlayCode;
    
    my $app = new Acme::PlayCode;
    
    $app->load_plugin('NumberPlus');
    
    my $played_code = $app->play( $code );
    # or
    my $played_code = $app->play( $filename );
    # or
    $app->play( $filename, { rewrite_file => 1 } ); # override $filename with played code

=head1 DESCRIPTION

    my $a = 1 + 2;

becomes

    my $a = 3; # 1 + 2

=head1 SEE ALSO

L<Acme::PlayCode>, L<Moose>, L<PPI>, L<MooseX::Object::Pluggable>

=head1 AUTHOR

Fayland Lam, C<< <fayland at gmail.com> >>

=head1 COPYRIGHT & LICENSE

Copyright 2008 Fayland Lam, all rights reserved.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=cut
