# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl 1.t'

#########################

use strict;
use Test::More;

my $tests = &func_tests;
plan tests => scalar @$tests;

BEGIN { use_ok('Text::Wigwam') };

#########################

# Insert your test code below, the Test::More module is use()ed here so read
# its man page ( perldoc Test::More ) for help writing this test script.

my $varspace = {};

sub do_fail{
    my( $test, $result, $message ) = @_;
    warn "\n";
    fail( $test->{testid} );
    warn "*** $message ***";
    warn ( $test->{perror} ? "Expected run-time error: " : $test->{terror} ? "Expected template error: " : "Expected parsed results: " );
    warn $test->{result};
    warn "Actual template results: $result" if $result;
}

foreach my $test ( @$tests ){
    my( $perror, $result );
    $varspace = $test->{vspace}
      if ref( $test->{vspace} ) eq 'HASH';
    my @params = ( @{$test->{params}}, Varspace => $varspace );
    if( my $wwobj = Text::Wigwam->new(@params) ){
#          No error was encountered.
        do_fail( $test, undef, "No template error was encountered when it was expected." ), next if( $test->{terror} );
        ( $perror, $result ) = $wwobj->execute( $varspace );
        if( $perror ){
#             A parse error was encountered
            if( !$test->{perror} ){ do_fail( $test, undef, "Encountered an unexpected parse error - $perror" ); }
            elsif( $perror =~ /$test->{result}/ ){ pass( $test->{testid} ); }
            else{ do_fail( $test, $perror, "The resulting parse error does not match the parse error that was expected - result: $perror" ); }
            next;
        }
        elsif( $test->{perror} ){
            do_fail( $test, undef, "No parse error was encountered when it was expected." );
            next;
        }
        elsif( $result =~ /$test->{result}/ ){ pass( $test->{testid} ); }
        else{ do_fail( $test, $result, "The results of the parsed template does not match what was expected." ); }
    }
    else{
        do_fail( $test, undef, "Unexpected template object creation error - $Text::Wigwam::ERROR" ), next if( !$test->{terror} );
        if( $Text::Wigwam::ERROR =~ /$test->{result}/ ){ pass( $test->{testid} ); }
        else{ do_fail( $test, $result, "The resulting template error does not match what was expected - $Text::Wigwam::ERROR." ); }
    }
}

sub func_tests{
    return [
        {
            testid => 'Engine Initialization',
            params => [
                text => '[!! #define opt #get_options opt.engine !!]',
                Restrict => { engine => 'Fusion' },
            ],
            result => '^Fusion$',
        },
        {
            testid => ' Variable Self-Interpolation',
            params => [ text => '[!! foo[x] !!]', Restrict => { engine => 'Fusion'}, Settings => { plugins => '' } ],
            result => '^foobar$',
            vspace => { fooz => 'foobar', x => 'z' },
        },
        {
            testid => ' Array Traversal',
            params => [ text => '[!! articles:0 !!]', Restrict => { engine => 'Fusion'}],
            result => '^blah blah blah$',
            vspace =>  {
                'articles' => [
                    "blah blah blah",
                    "yadda yadda yadda",
                ]
            },
        },
        {
            testid => ' Common Varspace',
            params => [ text => '[!! articles:1 !!]', Restrict => { engine => 'Fusion'} ],
            result => '^yadda yadda yadda$',
        },
        {
            testid => ' Multi-List Directives',
            params => [ text => '[!!#term_test "foo" "bar" #/* "bar" "foo" #/* "Pass"!!]', Restrict => { engine => 'Fusion'} ],
            result => '^foo,bar;bar,foo;Pass$',
            vspace => {},
        },
        {
            testid => ' Block Bit Preservation',
            params => [ text => '[!!#setup_ebbp #if #expr_block_preservation #get_expr_block_preservation_bit #get_expr_block_preservation_bit!!>Pass<!!  !!]', Restrict => { engine => 'Fusion', strict_tags => 1  } ],
            result => '^Pass$',
            vspace => {},
        },
        {
            testid => ' Implied Terminator - Block Immunity',
            params => [ text => '[!!#define Result { "Pass"!!][!!#if 0!!>[!!#define Result "Fail"!!][!!#define Result "Fail"!!]<!!  Result!!]', Restrict => { engine => 'Fusion', strict_tags => 0 } ],
            result => '^Pass$',
            vspace => {},
        },
        {
            testid => ' Block Terminator Association - Active',
            params => [ text => '[!!#define Result "Pass"!!][!!#if 0!!>[!!#/*!!][!!#define Result "Fail"!!]<!!  Result!!]', Restrict => { engine => 'Fusion', strict_tags => 0 } ],
            result => '^Pass$',
            vspace => {},
        },
        {
            testid => ' Block Terminator Association - Passive',
            params => [ text => '[!!#if 1!!>[!!#if 0!!><!! !!][!!#/*!!][!!#else!!>[!!#define Result "Pass"!!]<!! !!]<!!  Result!!]', Restrict => { engine => 'Fusion', strict_tags => 0, } ],
            result => '^Pass$',
            vspace => {},
        },
        {
            testid => 'Quoted Tags',
            params => [ text => '[!!#if #eq 8 #sizeof "<!!--!!>"!!>[!!#define Result "Pass"!!]<!!  Result!!]', Restrict => { engine => 'Fusion'} ],
            result => '^Pass$',
            vspace => {},
        },
        {
            testid => 'Passive get_arg Behavior',
            params => [ text => '[!! #setup_eft #or #eflag_test "Fail" "Pass" !!]', Restrict => { engine => 'Fusion'}, Settings => { plugins => 'DirectiveSet' } ],
            result => '^Pass$',
            vspace => {},
        },
        {
            testid => 'Strict Tag Boundary Enforcement - Active',
            params => [ text => '[!!#define foo!!>[!!#or!!][!!#define Result "Pass"!!]<!! !!][!! Result !!]', Restrict => { engine => 'Fusion', strict_tags => 1, } ],
            result => 'Missing argument:',
            perror => 1,
            vspace => {},
        },
        {
            testid => 'Strict Tag Boundary Enforcement - Passive',
            params => [ text => '[!!#define foo!!>[!!#if 0 #add!!][!!#Define Result "Pass"!!]<!!  Result!!]', Restrict => { engine => 'Fusion', strict_tags => 1, } ],
            result => 'Missing argument:',
            perror => 1,
            vspace => {},
        },
        {
            testid => 'Tag Boundary Limit - Active',
            params => [ text => '[!!#define foo!!>[!!#or!!][!!#define Result "Pass"!!]<!! !!][!! Result !!]', Restrict => { engine => 'Fusion', strict_tags => 0, } ],
            result => '^Pass$',
            vspace => {},
        },
        {
            testid => 'Tag Boundary Limit - Passive',
            params => [ text => '[!!#define foo!!>[!!#if 0 #add!!][!!#Define Result "Pass"!!]<!!  Result!!]', Restrict => { engine => 'Fusion', strict_tags => 0, } ],
            result => '^Pass$',
            vspace => {},
        },
        {
            testid => 'Multi-List Directives',
            params => [ text => '[!!#term_test "foo" "bar" #/* "bar" "foo" #/* "Pass"!!]', Restrict => { engine => 'Fusion'} ],
            result => '^foobar|barfoo|Pass$',
            vspace => {},
        },
        {
            testid => 'Passive Block Optimization - Block Inclusion',
            params => [ text => '[!! #eat #repeat 3 !!>[!! #if 0 !!>[!! #kill_block_test!!]<!!  !!] <!!  !!][!! #kill_block_test !!]', Restrict => { engine => 'Fusion'}, Defaults => { drip_cache => 1 } ],
            result => '^10$',
            vspace => {},
        },
        {
            testid => 'Passive Block Optimization - Non-Block Exclusion',
            params => [ text => '[!!#eat #repeat 3 #if 0!!>[!!#kill_block_nbe!!]<!! !!][!!#kill_block_nbe!!]', Restrict => { engine => 'Fusion'}, Defaults => { drip_cache => 1 } ],
            result => '^10$',
            vspace => {},
        },
        {
            testid => 'Drip Cache Optimization - Active',
            params => [ text => '[!!#eat #repeat 5 #dcache_opt #dcache_opt !!]', Restrict => { engine => 'Fusion'}, Defaults => { drip_cache => 0 } ],
            result => '^15$',
            vspace => {},
        },
        {
            testid => 'Drip Cache Optimization - Passive',
            params => [ text => '[!!#eat #repeat 5 #if 0 #dcache_opt #dcache_pasv_opt !!]', Restrict => { engine => 'Fusion'}, Defaults => { drip_cache => 0 } ],
            result => '^10$',
            vspace => {},
        },
        {
            testid => 'Passive Token Optimization',
            params => [ text => '[!!#eat #repeat 5 #if 0 #pasv_token_opt #ifelse #le #assign actual #pasv_token_opt 30 "Pass" { "Fail: " actual } !!]', Restrict => { engine => 'Fusion'}, Defaults => { drip_cache => 1 } ],
            result => '^Pass$',
            vspace => {},
        },
        {
            testid => 'Passive List Optimization',
            params => [ text => '[!! #eat #repeat 5 #if 0 { #pl_opt } #ifelse #le #assign actual #pl_opt 30 "Pass" { "Fail: " actual } !!]', Restrict => { engine => 'Fusion' }, Settings => { drip_cache => 1 } ],
            result => '^Pass$',
            vspace => {},
        },
        {
            testid => 'List Interrupt Optimization',
            params => [ text => '[!!#setup_dolist #sizeof #dolist 1 2 3 #break 4 5 #/* !!]', Restrict => { engine => 'Fusion', plugins => 'DirectiveSet' }, Settings => { drip_cache => 1 } ],
            result => '^4$',
            vspace => {},
        },
#        {
#            testid => 'Private Varspace',
#            params => [ text => '[!!test!!]', Restrict => { engine => 'Fusion'}, Settings => { local_varspace => 1 } ],
#            result => '^$',
#            vspace => { test => 'Fail' },
#        },
        {
            testid => 'Interpolated Directives',
            params => [ text => '[!! #proc doit !!>[!! #[action] 2 1 !!]<!!  !!][!!    #define action "add" #call doit () #define action "sub" #call doit () !!]', Restrict => { engine => 'Fusion'}, Settings => { } ],
            result => '31',
            vspace => {},
        },
    ]
}

package Text::Wigwam::Directives;

use Text::Wigwam::Const qw/:all/;
sub _proto_term_test{ [ TERM|STRING, TERM|STRING, STRING ] }
sub _term_test{
    my ($Api) = @_;
    join(
        ";" =>
        join( "," => @{$Api->get_arg} ),
        join( "," => @{$Api->get_arg} ),
        $Api->get_arg,
    );
}

sub _proto_setup_ebbp{ [] }
sub _setup_ebbp{ $_[0]->new_global( BLOCK => 'expr_block_preservation', 1 ); return; }

sub _proto_expr_block_preservation{ [ EXPR|ANY|BLOCK, EXPR|ANY ] }
sub _expr_block_preservation{
    $_[0]->set_global( 'expr_block_preservation', 0 );
    my $on_expr = $_[0]->get_arg;
    my $off_expr = $_[0]->get_arg;
    my $block_on = $on_expr->execute;
    my $block_off = $off_expr->execute;
    return( $block_on && !$block_off );
}

sub _proto_get_expr_block_preservation_bit{ [] }
sub _get_expr_block_preservation_bit{
    return $_[0]->get_global( 'expr_block_preservation' );
}

sub _proto_setup_eft{ [] }
sub _setup_eft{ $_[0]->new_eflag( LOCAL => 'eft' ); return; }

sub _proto_eflag_test{ [ STRING ], [ 'eft' ] }
sub _eflag_test{
    $_[0]->set_global( 'eft', 1 );
    return $_[0]->get_arg;
}

my $kill_block = 0;
sub _proto_kill_block_test{ $kill_block += 10; return [ ]; }
sub _kill_block_test{ return $kill_block++; }

my $kill_block_nbe = 0;
sub _proto_kill_block_nbe{ $kill_block_nbe += 10; return [ ]; }
sub _kill_block_nbe{ return $kill_block_nbe++; }

my $pasv_token_opt = 0;
sub _proto_pasv_token_opt{ $pasv_token_opt += 10; return [ ]; }
sub _pasv_token_opt{ return $pasv_token_opt++; }

my $pl_opt = 0;
sub _proto_pl_opt{ $pl_opt += 10; return [ ]; }
sub _pl_opt{ return $pl_opt++; }

sub _proto_setup_dolist{ [] }
sub _setup_dolist{
#    $_[0]->new_global( LOCAL => 'break' );
#    $_[0]->new_global( LOCAL => 'continue' );
    return;
}
sub _proto_dolist{ [ TERM|ANY ], [qw/break continue/] }
sub _dolist{ return $_[0]->get_arg }

my $dcache_opt = 0;
sub _proto_dcache_opt{ $dcache_opt += 10; return [ ]; }
sub _dcache_opt{ return $dcache_opt++; }

my $dcache_pasv_opt = 0;
sub _proto_dcache_pasv_opt{ $dcache_pasv_opt += 10; return [ ]; }
sub _dcache_pasv_opt{ return $dcache_pasv_opt++; }

