#!/usr/bin/perl

use strict;
use warnings;

use Test::More tests => 37;
use Test::Exception;

BEGIN {
	use_ok('Tree::Simple::Visitor');
};

use Tree::Simple;

my $SIMPLE_SUB = sub { "test sub" };
# execute this otherwise Devel::Cover gives odd stats
$SIMPLE_SUB->();

# check that we have a constructor
can_ok("Tree::Simple::Visitor", 'new');

# -----------------------------------------------
# test the new style interface
# -----------------------------------------------

my $visitor = Tree::Simple::Visitor->new();
isa_ok($visitor, 'Tree::Simple::Visitor');

my $tree = Tree::Simple->new(Tree::Simple->ROOT)
					   ->addChildren(
							Tree::Simple->new("1")
                                        ->addChildren(
                                            Tree::Simple->new("1.1"),
                                            Tree::Simple->new("1.2")
                                                        ->addChild(Tree::Simple->new("1.2.1")),
                                            Tree::Simple->new("1.3")
                                        ),
							Tree::Simple->new("2"),
							Tree::Simple->new("3"),
					   );
isa_ok($tree, 'Tree::Simple');

$tree->accept($visitor);

can_ok($visitor, 'getResults');
is_deeply(
        [ $visitor->getResults() ],
        [ qw(1 1.1 1.2 1.2.1 1.3 2 3)],
        '... got what we expected');

can_ok($visitor, 'setNodeFilter');

my $node_filter = sub { return "_" . $_[0]->getNodeValue() };
$visitor->setNodeFilter($node_filter);

can_ok($visitor, 'getNodeFilter');
is($visitor->getNodeFilter(), "$node_filter", '... got back what we put in');

# visit the tree again to get new results now
$tree->accept($visitor);

is_deeply(
        scalar $visitor->getResults(),
        [ qw(_1 _1.1 _1.2 _1.2.1 _1.3 _2 _3)],
        '... got what we expected');

# test some exceptions

throws_ok {
    $visitor->setNodeFilter();
} qr/Insufficient Arguments/, '... this should die';

throws_ok {
    $visitor->setNodeFilter([]);
} qr/Insufficient Arguments/, '... this should die';

# -----------------------------------------------
# test the old style interface for backwards
# compatibility
# -----------------------------------------------

# and that our RECURSIVE constant is properly defined
can_ok("Tree::Simple::Visitor", 'RECURSIVE');
# and that our CHILDREN_ONLY constant is properly defined
can_ok("Tree::Simple::Visitor", 'CHILDREN_ONLY');

# no depth
my $visitor1 = Tree::Simple::Visitor->new($SIMPLE_SUB);
isa_ok($visitor1, 'Tree::Simple::Visitor');

# children only
my $visitor2 = Tree::Simple::Visitor->new($SIMPLE_SUB, Tree::Simple::Visitor->CHILDREN_ONLY);
isa_ok($visitor2, 'Tree::Simple::Visitor');

# recursive
my $visitor3 = Tree::Simple::Visitor->new($SIMPLE_SUB, Tree::Simple::Visitor->RECURSIVE);
isa_ok($visitor3, 'Tree::Simple::Visitor');

# -----------------------------------------------
# test constructor exceptions
# -----------------------------------------------

# we pass a bad depth (string)
throws_ok {
	my $test = Tree::Simple::Visitor->new($SIMPLE_SUB, "Fail")
} qr/Insufficient Arguments \: Depth arguement must be either RECURSIVE or CHILDREN_ONLY/,
   '... we are expecting this error';

# we pass a bad depth (numeric)
throws_ok {
	my $test = Tree::Simple::Visitor->new($SIMPLE_SUB, 100)
} qr/Insufficient Arguments \: Depth arguement must be either RECURSIVE or CHILDREN_ONLY/,
   '... we are expecting this error';

# we pass a non-ref func argument
throws_ok {
	my $test = Tree::Simple::Visitor->new("Fail");
} qr/Insufficient Arguments \: filter function argument must be a subroutine reference/,
   '... we are expecting this error';

# we pass a non-code-ref func arguement
throws_ok {
	my $test = Tree::Simple::Visitor->new([]);
} qr/Insufficient Arguments \: filter function argument must be a subroutine reference/,
   '... we are expecting this error';

# -----------------------------------------------
# test other exceptions
# -----------------------------------------------

# and make sure we can call the visit method
can_ok($visitor1, 'visit');

# test no arg
throws_ok {
	$visitor1->visit();
} qr/Insufficient Arguments \: You must supply a valid Tree\:\:Simple object/,
   '... we are expecting this error';

# test non-ref arg
throws_ok {
	$visitor1->visit("Fail");
} qr/Insufficient Arguments \: You must supply a valid Tree\:\:Simple object/,
   '... we are expecting this error';

# test non-object ref arg
throws_ok {
	$visitor1->visit([]);
} qr/Insufficient Arguments \: You must supply a valid Tree\:\:Simple object/,
   '... we are expecting this error';

my $BAD_OBJECT = bless({}, "Test");

# test non-Tree::Simple object arg
throws_ok {
	$visitor1->visit($BAD_OBJECT);
} qr/Insufficient Arguments \: You must supply a valid Tree\:\:Simple object/,
   '... we are expecting this error';


# -----------------------------------------------
# Test accept & visit
# -----------------------------------------------
# Note:
# this test could be made more robust by actually
# getting results and testing them from the
# Visitor object. But for right now it is good
# enough to have the code coverage, and know
# all the pieces work.
# -----------------------------------------------

# now make a tree
my $tree1 = Tree::Simple->new(Tree::Simple->ROOT)
					   ->addChildren(
							Tree::Simple->new("1.0"),
							Tree::Simple->new("2.0"),
							Tree::Simple->new("3.0"),
					   );
isa_ok($tree1, 'Tree::Simple');

cmp_ok($tree1->getChildCount(), '==', 3, '... there are 3 children here');

# and pass the visitor1 to accept
lives_ok {
	$tree1->accept($visitor1);
} '.. this passes fine';

# and pass the visitor2 to accept
lives_ok {
	$tree1->accept($visitor2);
} '.. this passes fine';

# and pass the visitor3 to accept
lives_ok {
	$tree1->accept($visitor3);
} '.. this passes fine';

# ----------------------------------------------------
# test some misc. weirdness to get the coverage up :P
# ----------------------------------------------------

# check that includeTrunk works as we expect it to
{
    my $visitor = Tree::Simple::Visitor->new();
    ok(!$visitor->includeTrunk(), '... this should be false right now');

    $visitor->includeTrunk("true");
    ok($visitor->includeTrunk(), '... this should be true now');

    $visitor->includeTrunk(undef);
    ok($visitor->includeTrunk(), '... this should be true still');

    $visitor->includeTrunk("");
    ok(!$visitor->includeTrunk(), '... this should be false again');
}

# check that clearNodeFilter works as we expect it to
{
    my $visitor = Tree::Simple::Visitor->new();

    my $filter = sub { "filter" };

    $visitor->setNodeFilter($filter);
    is($visitor->getNodeFilter(), $filter, 'our node filter is set correctly');

    $visitor->clearNodeFilter();
    ok(! defined($visitor->getNodeFilter()), '... our node filter has now been undefined');
}


