package Froody::Repository;
use base qw(Froody::Base);

use Froody::API::Reflection;
use Froody::Invoker::Reflection;
use Froody::Dispatch;
use Froody::Invoker::Remote;

use Froody::Method;
use Froody::ErrorType;

use strict;
use warnings;

use Scalar::Util qw(blessed);

our $VERSION = 0.01;

=head1 NAME

Froody::Repository - a repository of Froody::Method objects.

=head1 SYNOPSIS

  use Froody::Repository;
  my $repository = Froody::Repository->new();

  # methods for putting stuff in the repository
  $repository->register_method($froody_method);
  $repository->register_errortype($froody_errortype);
  
  # methods for getting froody methods out
  my $method   = $repository->get_method('name.of.method');
  my @methods  = $repository->get_methods;
  my @methods2 = $repository->get_methods('namespace.of.interest');
  my @methods3 = $repository->get_methods(qr/delete/);
  
  # methods for getting froody errortype out
  my $errortype  = $repository->get_errortype('name.of.errortype');
  my @errortype  = $repository->get_errortypes;
  my @errortype2 = $repository->get_errortypes('errortypes.of.interest');
  my @errortype3 = $repository->get_errortypes(qr/delete/);
  my $errortype2 = $repository->get_closest_errortype('fragment.of.type');

=head1 DESCRIPTION

L<Froody::Repository> provides a central location to register and
discover L<Froody::Method> instances.

=head1 METHODS

=cut

sub init {
  my $self = shift; 

  ### reflection methods and errortypes ###

  my (@structures) = grep { blessed($_) && $_->isa("Froody::Structure") } 
      Froody::API::Reflection->load();

  my $invoker = Froody::Invoker::Reflection->new()->repository($self);

  foreach (grep { $_->isa("Froody::Method") } @structures) {
    #Note: Invoker only holds a weak reference to self -- no memory leaks here. Nope.
    $_->invoker($invoker); 
    $self->register_method($_);
  }

  foreach (grep { $_->isa("Froody::ErrorType") } @structures) {
    $self->register_errortype($_);
  }


  ### default error ###
  
  $self->register_errortype(Froody::ErrorType->new()->name(""));

  return $self;
}

=head2 Methods for dealing with Froody::Method instances

=over

=item register_method( Froody::Method )

Register a method with the repository.  You don't ever normally have
to call this method yourself, things like your Froody::Implementation
subclasses do it themselves from C<register_in_repository>.

=cut

sub register_method {
  my $self = shift;
  my $method = shift;

  Froody::Error->throw("perl.methodcall.param", "you didn't pass a Froody::Method object")
    unless UNIVERSAL::isa($method, 'Froody::Method');
  
  $self->{method_hash}{$method->full_name} ||= $method;

  return $self;
}

=item get_methods() || get_methods( $regex ) || get_methods( "foo.bar.*" )

Return all methods (as Froody::Method objects) if invoked with no arguments, or
only the methods matching the query if invoked with an argument.

=cut

sub get_methods {
  my $self = shift;
  
  # return everything if no arguments
  unless (@_) { return values %{ $self->{method_hash} } }

  # what methods are we looking for?  Make it a regex if it's not already
  my $query = ref($_[0]) ? shift : Froody::Method->match_to_regex( shift );
  return grep { $_->full_name =~ $query }
         values %{ $self->{method_hash} };
}

=item get_method($name)

Return a single method (as a Froody::Method object) matching $name exactly.
Throws a Froody::Error of type "froody.invoke.nosuchmethod" if no method
matching the $name is registered with this repository.

=cut

sub get_method {
  my $self = shift;
  my $name = shift;

  my $method = $self->{method_hash}{ $name }
    or Froody::Error->throw("froody.invoke.nosuchmethod", "Method '$name' not found",101);
  return $method;
}

=back

=head2 Methods for dealing with Froody::ErrorType instances

=over

=item register_errortype( Froody::ErrorType )

Register an errortype with the repository.  You don't ever normally have
to call this method yourself, things like your Froody::Implementation
subclasses do it themselves from C<register_in_repository>.

=cut

sub register_errortype {
  my $self = shift;
  my $errortype = shift;
  
  Froody::Error->throw("perl.methodcall.param", "you didn't pass a Froody::ErrorType object")
    unless UNIVERSAL::isa($errortype, 'Froody::ErrorType');
  
  $self->{errortypes_hash}{ $errortype->name } = $errortype;

  return $self;
}

=item get_errortype($name)

Return a single errortype (as a Froody::ErrorType object) matching $name exactly.
Throws a Froody::Error of type "froody.invoke.nosucherrortype" if no method
matching the $name is registered with this repository.

=cut

sub get_errortype {
  my $self = shift;
  my $name = shift;

  my $errortype = $self->{errortypes_hash}{ $name }
    or Froody::Error->throw("froody.invoke.nosucherrortype", "ErrorType '$name' not found");
  return $errortype;
}

=item get_errortypes() || get_errortypes( $regex ) || get_errortypes( "foo.bar.*" )

Return all errortypes (as Froody::ErrorTypes objects) if invoked with no
arguments, or only the methods matching the query if invoked with an argument.

=cut

sub get_errortypes {
  my $self = shift;
  
  # return everything if no arguments
  unless (@_) { return values %{ $self->{errortypes_hash} } }

  # what methods are we looking for?  Make it a regex if it's not already
  my $query = ref($_[0]) ? shift : Froody::Method->match_to_regex( shift );
  return grep { $_->name =~ $query }
         values %{ $self->{errortypes_hash} };
}

=item get_closest_errortype

=cut

sub get_closest_errortype {
  my $self = shift;
  my $code = shift;
  my @bits = split /\./, $code;
  
  foreach (reverse 0..$#bits)
  { 
     my $string = join '.', @bits[0..$_];
     return $self->{errortypes_hash}{ $string }
        if $self->{errortypes_hash}{ $string };
  }
  
  # return the default errortype hash which is always there, as it was
  # created during init.
  return $self->{errortypes_hash}{""};
}

=back

=head1 BUGS

None known

We're using C<match_to_regex> from Froody::Method, maybe that should
be refactored elsewhere

Please report any bugs you find via the CPAN RT system.
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Froody>

=head1 AUTHOR

Copyright Fotango 2005.  All rights reserved.

Please see the main L<Froody> documentation for details of who has worked
on this project.

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

=head1 SEE ALSO

L<Froody>, L<Froody::Method>

=cut

1;
