package Hash::Find;

use 5.008003;
use strict;
use warnings;

require Exporter;
require Cwd;


our @ISA = qw(Exporter);
@EXPORT = qw(find);

our $VERSION = '0.01';



# Takes two args
#  Input:
#       $callback   : A coderef to a callback function
#       @_:         :A  data structure to search.
sub find
{
    my $callback = shift;
    while (my $data = shift)
    {
        find_recurse($callback,$data);
    }
}



# The internal implmentation of find.
#
#   ARGS
#       $callback:  a coderef to a callback function
#       $base_data:      the base data structure to search
#       $path:      A string containing a path along the data structure.
sub find_recurse
{
    my $callback = shift;
    my $base_data = shift;
    my $path = shift || '';

    printi( "Evaluating arguments! \n");
    my $cur_item = eval('$base_data'.$path);
    if ($@)
    {
        print "Possible bug, or possibly the hash has been modified. Who knows?";
        warn $@;
        return;
    } ;
    
    # Test for the type of the current item

    unless (ref($cur_item)) # Scalars return undef from the 'ref' function
    {
        printi( "Arg is a scalar: $cur_item\n");
    } elsif (ref($cur_item) eq 'ARRAY')
    {
           printi( "Arg is an array\n");
           my $i =0;
           foreach my $elem (@$cur_item)
           {
               printi( "Arg Element\n");
               find_recurse($callback,$base_data,$path."->[$i]");
               $i++;
           }
    } elsif (ref($cur_item) eq 'HASH')
    {
           printi( "Arg is a hash\n");

        foreach my $tag (keys %$cur_item)
        {
            printi( "Tag: $tag\n");
            do_callback($callback,$base_data,$path,$tag);
            find_recurse($callback,$base_data,$path."->{'$tag'}");
        }        
    }
}

sub printi
{
    my $val = shift;
    if (0)
    {
        print $val;
    }
}



sub do_callback
{
    

    my $callback = shift;
    my $data = shift;
    my $path = shift;
    my $key = shift;
    
    my $return;
    $return->{'data'} = $data;
    $return->{'path'} = $path;
    $return->{'key'} = $key;
    $return->{'hash'} = eval('$data'.$path); # a hashref to the hash that contains the key.
    $return->{'value'} = eval('$data'.$path."->{'$key'}");
    no strict;
    &$callback($return);
}


1;
__END__




=head1 NAME

find - traverse a hash, perform callbacks for hash keys.

=head1 SYNOPSIS

    use Hash::Find;
    find(\&wanted, $hashref1, $hashref2, .....);
    sub wanted($)
    {
        my $cb_info = shift;
        print "Hash key is '$cb_info->{'key'}' \n";
        print "Hash value is " . Dumper($cb_info->{'value'}) . "\n";
        print "Path to data (as string) is '$cb_info->{'path'}' \n";
        print "Searched data element is '$cb_info->{'data'}' \n";
    }

=head1 DESCRIPTION

The first argument to find is a code reference.
The other arguments contain zero or more hashes, arrays or scalars you would like to traverse.

The wanted($) function does whatever verifications you want.
The only argument is a hash containing the following elements:

    $cb_info->{'key'}
        The key of the hash element
    $cb_info->{'value'}
        The value of the hash element
    $cb_info->{'path'}
        The path to the hash element as a string
        ex: '->{'west'}->{'side'}->[4]->{'eva'}'
    $cb_info->{'data'}
        The data item you are currently searching.
        
To use the path value effectively call as as follows:

    eval('$cb_info->{'data'}'.$cb_info->{'path'})->whatever you want to do here.
        
=head1 BUGS

No known bugs.


=cut





