
use v6;

use Tree::Visitor;

class Tree::Visitor::FindByPath-0.0.1 is Tree::Visitor;

has @!search_path;
has $!success;

sub set_search_path ($self: @path) {
    (@path) || die "Insufficient Arguments : You must specify a path";
    @!search_path = @path;
}

sub visit ($self: $tree) {

    # reset our success flag
    $!success = 0;

    # get our filter function
    my $func;
    if $self.has_node_filter() {
        $func = -> ($tree, $test) { (($self.get_node_filter().($tree) ~ "") eq $test) };    
    }
    else {
        $func = -> ($tree, $test) { (($tree.node() ~ "") eq $test) };  
    }

    # get ready with our results
    my @results;

    # get our path
    my @path = @!search_path;    

    # get our variables ready
    my $current_path;
    my $current_tree = $tree;

    # check to see if we have been 
    # asked to include the trunk
    if $self.include_trunk() {
        # if we dont match the root of the path
        # then we have failed already and so return
        return unless $func($current_tree, @path[0]);
        # if we do match, then store the tree in our results
        @results.push($current_tree);
    }

    loop {
        # if we have no more @path we have found it
        unless @path {
            # store the current tree as
            # our last result
            @results.push($current_tree);
            $self.set_results(@results);
            # and set the sucess flag
            $!success = 1;
            return;
        }
        # otherwise we need to keep looking ...
        # get the next element in the path
        $current_path = @path.shift;
        # now check all the current tree's children
        # for a match
        for $current_tree.get_all_children() -> $child {
            if $func($child, $current_path) {
                # if we find a match, then
                # we store the current tree 
                # in our results, and 
                @results.push($current_tree);
                # we change our current tree
                $current_tree = $child;
                # and go back to the TOP                
                next;
            }
        }
        # if we do not find a match, then we can fall off 
        # this block and the whole subroutine for that matter
        # since we know the match has failed.
    }
    # we do however, store the 
    # results as far as we got,
    # so that the user can maybe 
    # do something else to recover
    $self.set_results(@results);
}

sub get_result ($self:) {
    # if we did not succeed, then 
    # we return undef, ...
    return undef unless $!success;
    # otherwise we return the 
    # last in the results
    return ($self.get_results())[-1];
}
