package LookFor;
use Carp;
use strict;
our $AUTOLOAD;  # it's a package global


BEGIN {
 use Exporter   ();
 our(@ISA,@EXPORT);
 @ISA = qw(Exporter);
 @EXPORT =  qw(END_FIND FOUND DEBUG NOT_FOUND);
}

sub AUTOLOAD {
	my $self = shift;
	my $type = ref($self)
			or croak "<$self> is not an object";
	my $name = $AUTOLOAD;
	$name =~ s/.*://;   # strip fully-qualified portion
	unless (exists $self->{_permitted}->{$name} ) {
		croak "Can't access `$name' field in class $type";
	}
	if (@_) {
		return $self->{$name} = shift;
	} else {
		return $self->{$name};
	}
}


my %fields = ( #fields to inherit!
	data		=> undef, #Required in Class Init
	data_stream	=> undef, #"
	stream_name	=> undef, #"
	_data		=> undef,
	max_seeks       => 1000,
	seek		=> \&_default_seek,
	_parent		=> undef, #don't inheret
	_data		=> undef, #
	_end_find	=> undef,
	type 		=> undef,
	next_lookfor 	=> undef, 
	fnd_n_prc_data	=> undef, 
	name		=> undef
);

my @req_fields = qw/data data_stream stream_name/;

sub new {
 my $objq = shift;
 my $class = ref($objq) || $objq;
 my %init = @_;
 my $self  = {
	_permitted => \%fields,
	%fields,
	%init,
 };
 bless $self, $class;
 if(ref($objq)) {$self->_inst_check($objq,\%init) }
	 else {	$self->_check()}
 return $self;
}

sub _end_chk {
 my $self = shift;
 $_ =  $self->{sought};
 return FOUND()  if ("$_" eq undef);
 return NOT_FOUND();
}

sub _check {
 my $self = shift;
 $self->{_end_find} = $self->new("fnd_n_prc_data"=> \&_end_chk,"name"=> "end");
}

sub _inst_check {
 my ($self,$parent,$init) = @_;
 foreach (keys %fields) {
	if (!$$init{$_}) {$self->{$_} = $parent->{$_}}
 }
 $self->{_parent} = $parent;
 if ( ref($$init{fnd_n_prc_data}) ne "CODE" ) {
	$self->{_data}->{re} = $$init{fnd_n_prc_data};
 	$self->{fnd_n_prc_data} = \&_default_proc;
 }
}

sub change {
 my $self = shift;
 my %changes = @_;
 foreach my $param (keys %changes) {
	$self->{$param}  = $changes{$param};
 }
}

sub _default_proc {
my ($self,$srch_str) = @_;
 $_ = $self->{sought};
 if (/$self->{_data}->{re}/i) {
	if( $self->{type} & DEBUG() ) {
		print  "{".$self->{stream_name}." ".$self->{name}."}->$_\n";
	}
	return FOUND();
 }
 return NOT_FOUND();
}

sub _default_seek {
 my $self = shift;
 my $file = $self->{data_stream};
 $self->{sought} = <$file>;
 return $self->{sought};
}

#
#Insert Code Diagraming stuff here;)
#
sub next_lookfor {
 my $self = shift;
 if (@_ > 1) { #multiple lookfors!
	$self->{next_lookfor} = _combined_lookfors($self, @_);
 } else {
	$self->{next_lookfor} = _check_for_end_find($self,shift);
 }
}


sub _check_for_end_find { 
 my $self = shift;
 my $lookfor_in_question = shift;
 if ($lookfor_in_question eq "end_find" && ref($lookfor_in_question) ne "LookFor") {
	return $self->{_end_find};
 } else {
	return $lookfor_in_question;
 }
}

sub _combined_fnd_n_prc_data {
 my $self = shift; #self will be the combined lookfor...
 my $chk;
 foreach my $lookfor ( @{$self->{_data}->{lookfors}}) {
	$lookfor->{sought} = $self->{sought}; #The data to search changes!
	if($lookfor->{fnd_n_prc_data}($lookfor) eq FOUND()) {
		$self->{next_lookfor} = $lookfor->{next_lookfor};
		return FOUND();
	}
 }
 return return NOT_FOUND();
}

sub _combined_lookfors {
 my $self = shift;
 my $name;
 my @lookfors;
 while (@_) {
	my $lookfor = shift;
	$lookfor = _check_for_end_find($self,$lookfor);
	push @lookfors,$lookfor;
	$name .= "-{".$lookfor->{name}."}-";
 }
 my $combined_lookfor =
	$self->new( "fnd_n_prc_data"=>\&_combined_fnd_n_prc_data,
		"name" => $name);
 $combined_lookfor->{_data}->{lookfors} = \@lookfors;
 return $combined_lookfor;
}


sub lookfor {
 my $self = shift;
 my $chk = NOT_FOUND();
 my $sought = " ";
 my $debug = $self->{stream_name}."  |".$self->{name}."|";
 my $seek_count = 1;
 #foreach my $field (@req_fields) {
 #	croak "$field is not defined in new()!" unless ($self->{$field});
 #}
 while ($chk ne FOUND() && $sought ne undef && $seek_count < $self->{max_seeks}) {
	$sought = $self->{seek}($self);
        $chk = $self->{fnd_n_prc_data}($self);	
	$seek_count++;
 }
 croak "$debug END FOUND before pattern" unless ($sought || $self->{name} eq "end");
 croak "$debug: BROKEN LINK: Don't know what to do!"
	 unless ($self->{next_lookfor} || $self->{name} eq "end" );
 croak "$debug TOO MANY seeks!" if ($seek_count >= $self->{max_seeks});
 $seek_count = 1;
 $self->{next_lookfor}->lookfor() if ($self->{next_lookfor});
 return $self->{data};
}



sub DEBUG {return 1}
sub FOUND {return "found"}
sub NOT_FOUND {return "not found"}
sub END_FIND {return "end_find"}

return 1;