package Evo::Spy;
use Evo::Base -base;
use Hash::Util::FieldHash();

Hash::Util::FieldHash::fieldhash my %STORE;

sub _all_store($class) { \%STORE; }
sub _store($self)      { $self->_all_store->{$self} }

sub code { shift->_store->{code} }

sub origin {
  my $self = shift;
  return $self->_store->{origin} unless @_;
  $self->_store->{origin} = shift;
  $self;
}

sub calls($self) { $self->_store->{calls} //= [] }

sub _execute($class, $id, @args) {
  my $self  = Hash::Util::FieldHash::id_2obj($id);
  my $calls = $self->calls;
  my $want  = wantarray;
  my $ret   = [];
  my $call  = {args => \@args, wantarray => $want, return => $ret};
  push $calls->@*, $call;

  my $orig = $self->origin or return;

  return $orig->(@args) if !defined $want;
  return $want ? (@$ret = $orig->(@args)) : ($ret->[0] = $orig->(@args));
}

sub new {
  my ($class, %args) = @_;
  my $all = $class->_all_store;

  my $self = bless sub { $all->{(__SUB__)}->{code}->(@_) }, $class;

  my $store = $all->{$self} = {};
  my $id = Hash::Util::FieldHash::id $self;
  $store->{code} = sub { $class->_execute($id, @_) };
  $store->{origin} = $args{origin} if $args{origin};

  $self;

}

1;

# ABSTRACT: A basic spy function-object

__END__

=pod

=encoding UTF-8

=head1 NAME

Evo::Spy - A basic spy function-object

=head1 VERSION

version 0.0158

=head1 SYNOPSIS

  use Evo::Base -strict;
  use Evo::Spy;
  my $spy = Evo::Spy->new(origin => sub { say "hello ", shift });

  $spy->('foo');
  $spy->code->('bar');

  use Data::Dumper;
  say Dumper $spy->calls;

=head1 ATTRIBUTES

=head2 origin

An original code.

=head1 METHODS

=head2 new

Creates a new spy

=head2 code

Returns a code that can be used as a spy. Why to use it if a spy is already
a code itself? Because some code can check if passed argument is a code by
calling C<ref $spy> instead of C<reftype $spy>, and while the spy is an
instance of Evo::Spy or it's subclass, that code may not recognize a spy as a
subroutine ref.

  # Evo::Spy
  say ref $spy;

  # CODE
  say Scalar::Util::reftype $spy;

  # always CODE
  say ref $spy->code;
  say Scalar::Util::reftype $spy->code;

So here is a rule, use the shorter form for spying your own code, because
it's a shorter use L</"code"> if you get an exception
C<can not use ... as a subroutine ref..>

All variants do the same thing, the only difference is a type recognition

=head2 calls

Returns an array ref to information of all invocations of C<$spy->()> or
C<$spy->code()>. Don't rely on implementation and don't use this directly.
Use subclasses of L<Evo::Spy> instead which provide methods for that

=head1 AUTHOR

alexbyk.com

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by alexbyk.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
