package Net::HTTP::Spore;

# ABSTRACT: SPORE client

use Moose;

use IO::All;
use JSON;
use Carp;
use Try::Tiny;

use Net::HTTP::Spore::Core;

our $VERSION = 0.01;

sub new_from_string {
    my ($class, $string, %args) = @_;

    my $spec;

    try {
        $spec = JSON::decode_json($string);
    }catch{
        Carp::confess("unable to parse JSON spec: ".$_);
    };

    my ( $spore_class, $spore_object );
    # XXX should we let the possibility to override this super class, or add
    # another superclasses?

    $spore_class =
      Class::MOP::Class->create_anon_class(
        superclasses => ['Net::HTTP::Spore::Core'] );

    try {
        my $api_base_url;
        if ( $spec->{api_base_url} && !$args{api_base_url} ) {
            $args{api_base_url} = $spec->{api_base_url};
        }
        elsif ( !$args{api_base_url} ) {
            die "api_base_url is missing!";
        }

        if ( $spec->{api_format} ) {
            $args{api_format} = $spec->{api_format};
        }

        if ( $spec->{authentication} ) {
            $args{authentication} = $spec->{authentication};
        }

        $spore_object = $spore_class->new_object(%args);
        $spore_object = _add_methods( $spore_object, $spec->{methods} );

    }
    catch {
        Carp::confess( "unable to create new Net::HTTP::Spore object: " . $_ );
    };

    return $spore_object;
}

sub new_from_spec {
    my ( $class, $spec_file, %args ) = @_;

    Carp::confess("specification file is missing") unless $spec_file;

    my ( $content, $spec );

    if ( $spec_file =~ m!^http(s)?://! ) {
        my $uri     = URI->new($spec_file);
        my $req = HTTP::Request->new(GET => $spec_file);
        my $ua  = LWP::UserAgent->new();
        my $res = $ua->request( $req );
        $content = $res->content;
    }
    else {
        unless ( -f $spec_file ) {
            Carp::confess("$spec_file does not exists");
        }
        $content < io($spec_file);
    }

    $class->new_from_string( $content, %args );
}

sub _add_methods {
    my ($class, $methods_spec) = @_;

    foreach my $method_name (keys %$methods_spec) {
        $class->meta->add_spore_method($method_name,
            %{$methods_spec->{$method_name}});
    }
    $class;
}

1;


__END__
=pod

=head1 NAME

Net::HTTP::Spore - SPORE client

=head1 VERSION

version 0.02

=head1 SYNOPSIS

    my $client = Net::HTTP::Spore->new_from_spec('twitter.json');

    $client->enable('Format::JSON');
    $client->enable('Auth::OAuth');

    my $timeline = $client->public_timeline(format => 'json');
    my $tweets = $timeline->body;

    foreach my $tweet (@$tweets) {
        print $tweet->{user}->{screen_name}. " says ".$tweet->{text}."\n";
    }

    my $friends_timeline = $client->friends_timeline(format => 'json');

=head1 DESCRIPTION

This module is an implementation of the SPORE specification. To use this client, you need to use or to write a SPORE specification of an API. Some specifications are available L<http://github.com/SPORE/api-description>.

=head2 CLIENT CREATION

First you need to create a client. This can be done using two methods, B<new_from_spec> and B<new_from_string>. The client will read the specification file to create a appropriate methods to interact with the API.

=head2 MIDDLEWARES

It's possible to activate some middlewares to extend the usage of the client. If you're using an API that discuss in JSON, you can enable the middleware L<Net::HTTP::Spore::Middleware::JSON>.

    $client->enable('Format::JSON');

or only on some path

    $client->enable_if(sub{$_->[0]->path =~ m!/path/to/json/stuff!}, 'Format::JSON');

=head2 METHODS

=over 4

=item new_from_spec($specification_file, %args)

Create and return a L<Net::HTTP::Spore::Core> object, with methods
generated from the specification file. The specification file can
either be a file on disk or a remote URL.

=item new_from_string($specification_string, %args)

Create and return a L<Net::HTTP::Spore::Core> object, with methods
generated from the specification string.

=back

=head1 AUTHOR

franck cuny <franck@lumberjaph.net>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2010 by linkfluence.

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

