The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use strict;
use warnings;

package Net::FreshBooks::API::Iterator;
$Net::FreshBooks::API::Iterator::VERSION = '0.24';
use Moose;

use Lingua::EN::Inflect qw( PL );
use XML::LibXML qw( XML_ELEMENT_NODE );

has 'parent_object' => ( is => 'rw' );    # The object we are iterating for
has 'args'          => ( is => 'rw' );    # args used in the search
has 'total'         => ( is => 'rw' );    # The total number of results
has 'pages'         => ( is => 'rw' );    # the number of result pages
has 'item_nodes'    => ( is => 'rw' );    # a list of all items
has 'current_index' => ( is => 'rw' );    # which item we are currently on

sub new {
    my $class = shift;
    my $self = bless shift, $class;

    my $request_args = {
        _method => $self->parent_object->method_string( 'list' ),

        # defaults
        page     => 1,
        per_page => 15,

        %{ $self->args },
    };

    my $list_name = PL( $self->parent_object->api_name );

    my $response = $self->parent_object->send_request( $request_args );
    my ( $list ) = $response->findnodes( "//$list_name" );

    $self->pages( $list->getAttribute( 'pages' ) );
    $self->total( $list->getAttribute( 'total' ) );

    my $parser = XML::LibXML->new();

    my @item_nodes =    #
        map  { $parser->parse_string( $_->toString ) }    # recreate
        grep { $_->nodeType eq XML_ELEMENT_NODE }         # filter
        $list->childNodes;

    $self->item_nodes( \@item_nodes );

    return $self;
}

sub next {                                                ## no critic
    ## use critic
    my $self = shift;

    # work out what the current index should be
    my $current_index = $self->current_index;
    $current_index = defined( $current_index ) ? $current_index + 1 : 0;
    $self->current_index( $current_index );

    # check that there is a next item
    # FIXME - add fetching the next page if needed here
    my $next_node = $self->item_nodes->[$current_index];
    return if !$next_node;

    # if we don't clone here, a user who iterates and pushes the returned
    # objects to a list will end up with a list of copies of the last
    # object to be returned
    return $self->parent_object->clone->_fill_in_from_node( $next_node );
}

__PACKAGE__->meta->make_immutable( inline_constructor => 0 );

1;

# ABSTRACT: FreshBooks Iterator objects

__END__

=pod

=encoding UTF-8

=head1 NAME

Net::FreshBooks::API::Iterator - FreshBooks Iterator objects

=head1 VERSION

version 0.24

=head1 SYNOPSIS

You should never need to create an Iterator yourself.  Iterators are created
via the list() method.  However, you should be aware that iterators do not
currently handle pagination for you.  So, something like this will let you
page through your results.

    my $page     = 1;
    my $per_page = 50;
    while ( 1 ) {
        my $invoices
            = $fb->invoice->list( { page => $page, per_page => $per_page } );
        while ( my $invoice = $invoices->next ) {
            print $invoice->invoice_id . "\n";
        }
        last if $page >= $invoices->pages;
        ++$page;
    }

=head2 new

    my $iterator = $class->new(
        {   parent_object => $parent_object,
            args         => {...},
        }
    );

Create a new iterator object. As part of creating the iterator a request is sent
to FreshBooks.

=head2 next

    my $next_result = $iterator->next(  );

Returns the next item in the iterator.

=head2 total

Returns the total number of results available, regardless of how many items are
on the current page.

=head2 pages

Returns the total number of result pages

=head2 current_index

The item we are currently on

=head1 AUTHORS

=over 4

=item *

Edmund von der Burg <evdb@ecclestoad.co.uk>

=item *

Olaf Alders <olaf@wundercounter.com>

=back

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2011 by Edmund von der Burg & Olaf Alders.

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