use strict;
use warnings;

use Data::Dump::Streamer;
use Test::More tests => 14;
require "./t/test_helper.pl";
diag "\nPadWalker ",
    eval "use PadWalker 0.99; 1" ? qq($PadWalker::VERSION is) : "isn't",
    " installed";

$::No_Redump= $::No_Redump= 1;
$::No_Dumper= $::No_Dumper= 1;

{
    my $v= 'foo';
    my @v= ('f', 'o', 'o');
    my $z= 1;
    no warnings;

    sub get_sub {
        my @v= (@v, 1);
        my @y= ('b', 'a', 'r');
        my $x= join " ", @_, @v, $v, $z;

        sub {
            my @y= ($x, "A" .. "G", @y);
            my @v= ("M" .. "R", @v);
            my $x= join ":", @y, @v, $z || 'undef';
            $x . "!!";
        }, sub { $x= shift; $z= shift if @_; }, do {
            my @y= split //, 'fuzz';
            sub { return join "+", $z, $x, @y; }
        },;

    }
}

{
    my $expect;
    if ($] >= 5.013_001) {
        $expect= <<'EXPECT';
my ($x,$z,@v,@y,@y_eclipse_1);
$x = 'f o o 1 foo 1';
$z = 1;
@v = (
       'f',
       ( 'o' ) x 2,
       1
     );
@y = (
       'b',
       'a',
       'r'
     );
@y_eclipse_1 = (
                 'f',
                 'u',
                 ( 'z' ) x 2
               );
$CODE1 = sub {
           my(@y) = ($x, ('A', 'B', 'C', 'D', 'E', 'F', 'G'), @y);
           my(@v) = (('M', 'N', 'O', 'P', 'Q', 'R'), @v);
           my $x = join(':', @y, @v, $z || 'undef');
           $x . '!!';
         };
$CODE2 = sub {
           $x = shift();
           $z = shift() if @_;
         };
$CODE3 = sub {
           return join('+', $z, $x, @y_eclipse_1);
         };

EXPECT
    }
    else {
        $expect= <<'EXPECT';
my ($x,$z,@v,@y,@y_eclipse_1);
$x = 'f o o 1 foo 1';
$z = 1;
@v = (
       'f',
       ( 'o' ) x 2,
       1
     );
@y = (
       'b',
       'a',
       'r'
     );
@y_eclipse_1 = (
                 'f',
                 'u',
                 ( 'z' ) x 2
               );
$CODE1 = sub {
           my(@y) = ($x, ('A', 'B', 'C', 'D', 'E', 'F', 'G'), @y);
           my(@v) = (('M', 'N', 'O', 'P', 'Q', 'R'), @v);
           my $x = join(':', @y, @v, $z || 'undef');
           $x . '!!';
         };
$CODE2 = sub {
           $x = shift @_;
           $z = shift @_ if @_;
         };
$CODE3 = sub {
           return join('+', $z, $x, @y_eclipse_1);
         };

EXPECT
    }

    test_dump('Lexicals!!', scalar(Dump()), (get_sub()), $expect);
}

{
#    local $Data::Dump::Streamer::DEBUG=1;

    my $x;
    $x= sub { $x };

    test_dump("Self-referential", scalar(Dump()), ($x), <<'EXPECT');
$x = sub {
       $x;
     };
EXPECT
}

{
    my $a;
    my $b= sub { $a };

    test_dump("Nested closure with shared state",
        scalar(Dump()), (sub { $a, $b }), <<'EXPECT');
my ($a,$b);
$a = undef;
$b = sub {
       $a;
     };
$CODE1 = sub {
           $a, $b;
         };
EXPECT
}

{

    my $a;
    my $b;
    my $z= sub { $a, $b };
    my $y= do {
        my $b; sub { $a, $b }
    };
    test_dump("Overlapping declarations", scalar(Dump()), ($y, $z), <<'EXPECT');
my ($a,$b,$b_eclipse_1);
$a = undef;
$b = undef;
$b_eclipse_1 = undef;
$CODE1 = sub {
           $a, $b;
         };
$CODE2 = sub {
           $a, $b_eclipse_1;
         };
EXPECT
}

{

    my $a;
    my $z= sub { $a };
    my $b;
    my $y= sub { $a, $b };

    test_dump("Overlapping declarations two",
        scalar(Dump()), ($y, $z), <<'EXPECT');
my ($a,$b);
$a = undef;
$b = undef;
$CODE1 = sub {
           $a, $b;
         };
$CODE2 = sub {
           $a;
         };
EXPECT
}

{

    my $z= do {
        my $a;
        sub { $a };
    };
    my $y= do {
        my $a;
        sub { $a };
    };

    test_dump("Unrelated environments", scalar(Dump()), ($z, $y), <<'EXPECT');
my ($a,$a_eclipse_1);
$a = undef;
$a_eclipse_1 = undef;
$CODE1 = sub {
           $a;
         };
$CODE2 = sub {
           $a_eclipse_1;
         };
EXPECT
}

{
    my $bad= \&Not::Implemented;
    test_dump("Unimplemented code", scalar(Dump()), ($bad), <<'EXPECT');
$CODE1 = \&Not::Implemented;
EXPECT
}

{
    my $a;
    my $z= sub { $a };

    test_dump(
        "Shared state/enclosed", scalar(Dump()),
        ($z, sub { $a, $z }),    <<'EXPECT');
my ($a);
$a = undef;
$z = sub {
       $a;
     };
$CODE1 = sub {
           $a, $z;
         };
EXPECT

    test_dump(
        "Named  Shared state/enclosed",
        scalar(Dump())->Names('foo', 'bar'),
        ($z, sub { $a, $z }), <<'EXPECT');
my ($a);
$a = undef;
$foo = sub {
         $a;
       };
$bar = sub {
         $a, $foo;
       };
EXPECT
}
{

    no warnings;
    our $b;
    my $a;
    my $b= sub { $b };

    test_dump("sub b", scalar(Dump()), ($b), <<'EXPECT');
$CODE1 = sub {
           $b;
         };
EXPECT

    test_dump("double sub b", scalar(Dump()), (sub { $b }), <<'EXPECT');
my ($b);
$b = sub {
       $b;
     };
$CODE1 = sub {
           $b;
         };
EXPECT

}
{

    my $a= "foo";
    my $x= sub { return $a . "bar" };
    sub f { print $x->() }
    test_dump("recursively nested subs", scalar(Dump()), (\&f), <<'EXPECT');
my ($a,$x);
$a = 'foo';
$x = sub {
       return $a . 'bar';
     };
$CODE1 = sub {
           print &$x();
         };
EXPECT
}
{
    test_dump(
        "EclipseName",
        Dump->EclipseName('%d_foiled_%s'), ([
                map {
                    my $x;
                    my $x_eclipse_1;
                    sub { $x }, sub { $x_eclipse_1 };
                } 1,
                2
            ]
        ),
        <<'EXPECT');
my ($1_foiled_x,$1_foiled_x_eclipse_1,$x,$x_eclipse_1);
$1_foiled_x = undef;
$1_foiled_x_eclipse_1 = undef;
$x = undef;
$x_eclipse_1 = undef;
$ARRAY1 = [
            sub {
              $x;
            },
            sub {
              $x_eclipse_1;
            },
            sub {
              $1_foiled_x;
            },
            sub {
              $1_foiled_x_eclipse_1;
            }
          ];

EXPECT

}
{
    test_dump(
        "EclipseName 2",
        Dump->EclipseName('%s_muhaha_%d'), ([
                map {
                    my $x;
                    my $x_eclipse_1;
                    sub { $x }, sub { $x_eclipse_1 };
                } 1,
                2
            ]
        ),
        <<'EXPECT');
my ($x,$x_eclipse_1,$x_eclipse_1_muhaha_1,$x_muhaha_1);
$x = undef;
$x_eclipse_1 = undef;
$x_eclipse_1_muhaha_1 = undef;
$x_muhaha_1 = undef;
$ARRAY1 = [
            sub {
              $x;
            },
            sub {
              $x_eclipse_1;
            },
            sub {
              $x_muhaha_1;
            },
            sub {
              $x_eclipse_1_muhaha_1;
            }
          ];
EXPECT

}

if (0) {

    #no warnings;
    my @close;
    my ($x, $y)= (3.141, 5);
    for my $a ($x, $y) {
        for my $b ($x, $y) {
            push @close, sub { ++$a, ++$b; return }
                if \$a != \$b;
        }
    }
    my $out= Dump(\@close)->Out();
    print $out;

    #print B::Deparse::WARN_MASK;
}

__END__
