#!/usr/bin/perl
# 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;

my $tdispatch = {
    CODE => sub {
        my ( $vsobj, $test ) = @_;
        my ( $params ) = $test->{params};
        my $res = (shift @{$test->{params}})->(
            $vsobj,
            @$params,
        );
        return comp_test($test, $res);
    },
    get_value => sub {
        my ( $vsobj, $test ) = @_;
        my $res = $vsobj->get_value( @{$test->{params}} );
        return comp_test($test, $res);
    },
    set_value => sub {
        my ( $vsobj, $test ) = @_;
        my $res = $vsobj->set_value( @{$test->{params}} );
        return comp_test($test, $res);
    },
    is_defined => sub {
        my ( $vsobj, $test ) = @_;
        my ( $params, $tresult ) = @$test{qw/params result/};
        return bool_test(
            $test,
            $vsobj->is_defined(@$params),
        );
    },
    interpolate => sub {
        my ( $vsobj, $test ) = @_;
        my $res = $vsobj->interpolate( @{$test->{params}} );
        return comp_test($test, $res);
    },
    escape => sub {
        my ( $vsobj, $test ) = @_;
        my $res = $vsobj->escape( @{$test->{params}} );
        return comp_test($test, $res);
    },
    EATIT => sub {
        my ( $vsobj, $test ) = @_;
        do_fail(
            $test,
            undef,
            join(
              " " => (
                "Test",
                $test->{method},
                "is not implemented!",
              )
            )
        )
    },
};

BEGIN { use_ok('Text::Wigwam::Stash') };
use Text::Wigwam::Globals;

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

# 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 $stash = {};
my $globals = Text::Wigwam::Globals->new;

$globals->new_global( GLOBAL => test => "pass" );
$globals->new_global( GLOBAL => hash => {} );

sub bool_test {
    my ( $test, $result ) = @_;
    $result ? $test->{result}  ? do_pass($test)
                               : do_fail($test, $result)
            : !$test->{result} ? do_pass($test)
                               : do_fail($test, $result);
}

sub comp_test {
    my ( $test, $result ) = @_;
    return do_fail($test, $result)
      if defined $result xor defined $test->{result};
    return do_fail($test, $result)
        if $result ne $test->{result};
    do_pass($test);
}

sub do_fail {
    my( $test, $result, $message ) = @_;
    warn "\n";
    fail( $test->{testid} );
    warn "*** $message ***" if defined($message);
    warn
        join( "" =>
          (
            $test->{perror} ? "Expected run-time error: "
          : $test->{terror} ? "Expected template error: "
                            : "Expected results: "
          ),
        $test->{result},
    );
#    warn $test->{result};
    my $out
       = !defined $result ? "Produced an undefined value"
      :  length($result)  ? "  Actual results: $result\n"
                          : "Produced a null string value"
    ;
    warn "$out\n";
}

sub do_pass {
    my ( $test ) = @_;
    pass( $test->{method}.": ".$test->{testid} );
}

foreach my $test ( @$tests ){
#    if( $test->{control} && $test->{control} eq 'skip' ){
#        skip('bite me', 1);
#        next;
#    }
    $stash = $test->{vspace} if ref( $test->{vspace} ) eq 'HASH';
    if( my $vsobj = Text::Wigwam::Stash->new( varspace => $stash, globals => $globals ) ){
        (
            $tdispatch->{$test->{method}}
         || $tdispatch->{EATIT}
        )->($vsobj, $test);
    }
    else{
        if( !$test->{terror} ) {
            do_fail(
                $test,
                undef,
                "Unexpected stash object creation error - $@"
            );
        }
    }
}

sub func_tests {
    return [
        {
            testid => ' Root Level Grab',
            method => 'get_value',
            params => [ 'one' ],
            result => 'uno',
            vspace =>  { one => 'uno' },
        },
        {
            testid => ' Nested Hash Grab',
            method => 'get_value',
            params => [ '1.one' ],
            result => 'uno',
            vspace =>  { 1 => { one => 'uno' } },
        },
        {
            testid => ' Globals Access',
            method => 'get_value',
            params => [ ':test' ],
            result => 'pass',
            vspace =>  { },
        },
        {
            testid => ' Globals Manipulation A',
            method => 'set_value',
            params => [ ':hash.key', 'Pass' ],
            result => 'Pass',
            vspace =>  { },
        },
        {
            testid => ' Globals Manipulation B',
            method => 'get_value',
            params => [ ':hash.key' ],
            result => 'Pass',
            vspace =>  { },
        },
        {
            testid => ' Globals Access',
            method => 'get_value',
            params => [ '[:test].one' ],
            result => 'uno',
            vspace =>  { pass => { one => 'uno' } },
        },
        {
            testid => ' Indirect Grab',
            method => 'get_value',
            params => [ '[one]' ],
            result => '1',
            vspace => { one => 'uno', uno => 1 },
        },
        {
            testid => ' Indirect Grab',
            method => 'interpolate',
            params => [ '[one]' ],
            result => 'uno',
            vspace => { one => 'uno', uno => 1 },
        },
        {
            testid => ' Nested Brackets',
            method => 'get_value',
            params => [ '[one[two]]' ],
            result => 'tres',
            vspace => {
                one    => 'uno',
                two    => 'dos',
                onedos => 'three',
                three  => 'tres'
            },
        },
        {
            testid => ' Nested Brackets',
            method => 'interpolate',
            params => [ '[one[two]]' ],
            result => '1',
            vspace => { one => 'uno', two => 'dos', 'onedos' => 1 },
        },
        {
            testid => ' Multiple Nested Brackets',
            method => 'interpolate',
            params => [ '[[one][two]]' ],
            result => 'three',
            vspace => {
                one    => 'uno',
                two    => 'dos',
                unodos => 'three',
                three  => 'tres'
            },
        },
        {
            testid => ' Multiple Nested Brackets',
            method => 'get_value',
            params => [ '[[one][two]]' ],
            result => 'tres',
            vspace => {
                one    => 'uno',
                two    => 'dos',
                unodos => 'three',
                three  => 'tres'
            },
        },
        {
            testid => ' Extended Nested Brackets',
            method => 'interpolate',
            params => [ 'x[[one][two]]' ],
            result => 'xthree',
            vspace => {
                one    => 'uno',
                two    => 'dos',
                unodos => 'three',
                xthree => 'xtres'
            },
        },
        {
            testid => 'Extended Nested Brackets',
            method => 'get_value',
            params => [ 'x[[one][two]]' ],
            result => 'xtres',
            vspace => {
                one    => 'uno',
                two    => 'dos',
                unodos => 'three',
                xthree => 'xtres'
            },
        },
        {
            testid => 'Escaped Brackets',
            method => 'interpolate',
            params => [ '\\[Escaped\\:\\]\\[\\]\\[\\[\\]\\][one]\\[two\\]' ],
            result => '\\[Escaped\\:\\]\\[\\]\\[\\[\\]\\]uno\\[two\\]',
            vspace => {
                one => 'uno',
                '[Escaped:][][[]]uno[two]' => 'made it through that mess'
            },
        },
        {
            testid => 'Escaped Brackets',
            method => 'get_value',
            params => [ '\\[Escaped\\:\\]\\[\\]\\[\\[\\]\\][one]\\[two\\]' ],
            result => 'made it through that mess',
            vspace => {
                one => 'uno',
                '[Escaped:][][[]]uno[two]' => 'made it through that mess'
            },
        },
        {
            testid => 'Escaped Brackets',
            method => 'interpolate',
            params => [
                '[\\[Escaped\\:\\]\\[\\]\\[\\[\\]\\][one]\\[two\\]]'
            ],
            result => 'hairy',
            vspace => {
                one => 'uno',
                '[Escaped:][][[]]uno[two]' => 'hairy',
                hairy =>   'made it through that mess'
            },
        },
        {
            testid => 'Escaped Brackets',
            method => 'interpolate',
            params => [
                '[\\[Escaped\\:\\]\\[\\]\\[\\[\\]\\][one]\\[two\\]\\\\]'
            ],
            result => 'hairy',
            vspace => {
                one => 'uno',
                '[Escaped:][][[]]uno[two]\\' => 'hairy',
                hairy =>   'made it through that mess'
            },
        },
        {
            testid => 'Escaped Brackets',
            method => 'get_value',
            params => [
                '[\\[Escaped\\:\\]\\[\\]\\[\\[\\]\\][one]\\[two\\]]'
            ],
            result => 'made it through that mess',
            vspace => {
                one => 'uno',
                '[Escaped:][][[]]uno[two]' => 'hairy',
                hairy =>   'made it through that mess'
            },
        },
        {
            testid => 'Escaping Colons',
            method => 'escape',
            params => [ 'a::b::c' ],
            result => 'a\\:\\:b\\:\\:c',
            vspace => { },
        },
        {
            testid => 'Escaped Colons',
            method => 'get_value',
            params => [ 'a\\:\\:b\\:\\:c' ],
            result => 'def',
            vspace => { 'a::b::c' => 'def' },
        },
        {
            testid => 'Escaping Colons With Backslashes',
            method => 'escape',
            params => [ '\\a::\\b::\\c' ],
            result => '\\\\a\\:\\:\\\\b\\:\\:\\\\c',
            vspace => { },
        },
#18
        {
            testid => 'Escaped Colons With Backslashes',
            method => 'get_value',
            params => [ '\\\\a\\:\\:\\\\b\\:\\:\\\\c' ],
            result => 'def',
            vspace => { '\\a::\\b::\\c' => 'def' },
        },
        {
            testid => 'Escaped Colons With Hash Lookups',
            method => 'get_value',
            params => [ 'hash.one\\:\\:two\\:\\:three\\:\\:foo.bar' ],
            result => 'bazz',
            vspace => {
                hash => {
                    'one::two::three::foo' => { bar => 'bazz' }
                }
            },
        },
        {
            testid => 'Escaped Dots and Colons',
            method => 'get_value',
            params => [ '\\.a.b\\:c' ],
            result => 'blah',
            vspace => { '.a' => { 'b:c' => 'blah' } },
        },
        {
            testid => 'Escaped Trailing Dots and Colons (get_value)',
            method => 'is_defined',
            params => [ 'a\.b.c\.d' ],
            result => '1',
            vspace => { 'a.b' => { 'c.d' => 'boo' } },
        },
        {
            testid => 'Escaped Trailing Dots and Colons (is_defined)',
            method => 'is_defined',
            params => [ 'a\.b.c\.d' ],
            result => '1',
        },
        {
            testid => 'Undesirable key vivication (is_defined)',
            method => 'CODE',
            params => [
                sub {
                    my ( $vspace ) = @_;
                    $vspace->is_defined( 'a.a' );
                    return join( ", " => sort keys %{$vspace->get_value('[]')} );
                },
            ],
            vspace => { x => 1, y => 1, z => 1 },
            result => 'x, y, z',
        },
        {
            testid => 'Undesirable key vivication (undefine)',
            method => 'CODE',
            params => [
                sub {
                    my ( $vspace ) = @_;
                    $vspace->undefine( 'a.b' );
                    return join( ", " => sort keys %{$vspace->get_value('[]')} );
                },
            ],
            vspace => { x => 1, y => 1, z => 1 },
            result => 'x, y, z',
        },
 #       {
 #           testid => 'Undesirable key vivication (get_value)',
 #           method => 'CODE',
 #           control => 'skip',
 #           params => [
 #               sub {
 #                   my ( $vspace ) = @_;
 #                   my $val = $vspace->get_value( 'x.z.y' );
 #                   return join( ", " => sort keys %{$vspace->get_value('[]')} );
 #               },
 #           ],
 #           vspace => { y => 1, z => 1 },
 #           result => '',
 #       },

##
## More tests to come..
##

#        {
#            testid => 'Test name',
#            method => 'interpolate',
#            params => [ '' ],
#            result => '',
#            vspace => { '_' => undef },
#        },
    ]
}


1;
