# ABSTRACT: A collection of Net::Object::Peer::Subscriptions
package Net::Object::Peer::Subscriptions;

use 5.10.0;

use Types::Standard qw[ ArrayRef InstanceOf ];
use Ref::Util qw[ is_coderef ];
use List::Util qw[ all ];
use Safe::Isa;

use Net::Object::Peer::Subscription;
use Net::Object::Peer::Subscription::Ephemeral;


use Moo;
use strictures 2;
use namespace::clean;

our $VERSION = '0.04'; # TRIAL

has _subscriptions => (
    is       => 'ro',
    init_arg => undef,
    isa      => ArrayRef [ InstanceOf ['Net::Object::Peer::Subscription'] ],
    default => sub { [] },
    lazy    => 1,
);

#pod =method list
#pod
#pod   @subs = $subs->list;
#pod
#pod Returns a list of hashrefs containing attributes for all subscriptions.
#pod
#pod
#pod =cut

sub list {
    return map { $_->as_hashref } @{ $_[0]->_subscriptions };
}

#pod =method nelem
#pod
#pod   $nelem = $subs->nelem;
#pod
#pod return the number of elements in the list of subscriptions.
#pod
#pod =cut

sub nelem {
    return scalar @{ $_[0]->_subscriptions };
}

#pod =method add
#pod
#pod   $subs->add( %attr );
#pod
#pod Add a subscription.  See L<Net::Object::Peer::Subscription> for the
#pod supported attributes.
#pod
#pod =cut

sub add {
    my $self = shift;

    my %attr = ( @_ == 1 ? %{ $_[0] } : @_ );

    my $class = 'Net::Object::Peer::Subscription';
    $class .= '::Ephemeral' if $attr{peer}->$_does( 'Net::Object::Peer::Ephemeral' );

    push @{ $self->_subscriptions }, $class->new( %attr );

    return;
}

sub _find_index {

    my $self = shift;
    my $subs = $self->_subscriptions;

    if ( is_coderef( $_[0] ) ) {

        my $match = shift;
        return grep { $match->( $subs->[$_] ) } 0 .. @$subs - 1;

    }
    else {
        my %match = @_;

        return grep {
            my $sub = $subs->[$_];
            all {
                if ( $sub->can( $_ ) ) {
                    my $val = $sub->$_;

                        !defined $val && !defined $match{$_} ? 1
                      : !defined $val || !defined $match{$_} ? 0
                      :   $val eq $match{$_};
                }
                else {
                    0;
                }
            }
            keys %match;
        } 0 .. @$subs - 1;
    }
}

#pod =method find
#pod
#pod   my @subs = $subs->find( $coderef | %spec );
#pod
#pod Returns a list of hashrefs containing attributes for subscriptions
#pod which match the passed arguments.
#pod
#pod A single argument must be a coderef; it will be invoked with a
#pod L<Net::Peer::Subscription> object as an argument.  It should return
#pod true if it matches, false otherwise.
#pod
#pod If a hash is passed, its values are compared to the attributes of
#pod subscriptions in the list.
#pod
#pod =cut

sub find {

    my $self = shift;

    my $subs = $self->_subscriptions;

    return unless @_;

    my @indices = $self->_find_index( @_ );
    return map { $_->as_hashref } @{$subs}[@indices];
}


#pod =method remove
#pod
#pod   @hashrefs = $subs->remove( $coderef | %spec );
#pod
#pod Unsubscribe the matching subscriptions, remove them from the list of
#pod subscriptions, and return hashrefs containing the subscriptions' event
#pod names and peers.
#pod
#pod
#pod =cut

sub remove {

    my $self = shift;

    my $subs = $self->_subscriptions;

    my @subs;

    if ( @_ ) {
        # need to remove subscriptions from the back to front,
        # or indices get messed up
        my @indices = reverse sort $self->_find_index( @_ );

        @subs = reverse map { splice( @$subs, $_, 1 ) } @indices;
    }

    else {
        @subs  = @$subs;
        @$subs = ();
    }

    $_->unsubscribe foreach @subs;

    return map { $_->as_hashref } @subs;
}

1;
#
# This file is part of Net-Object-Peer
#
# This software is Copyright (c) 2016 by Smithsonian Astrophysical Observatory.
#
# This is free software, licensed under:
#
#   The GNU General Public License, Version 3, June 2007
#

=pod

=head1 NAME

Net::Object::Peer::Subscriptions - A collection of Net::Object::Peer::Subscriptions

=head1 VERSION

version 0.04

=head1 DESCRIPTION

A B<Net::Object::Peer::Subscriptions> object manages a collection
of L<Net::Object::Peer::Subscriptions> objects.

=head1 METHODS

=head2 list

  @subs = $subs->list;

Returns a list of hashrefs containing attributes for all subscriptions.

=head2 nelem

  $nelem = $subs->nelem;

return the number of elements in the list of subscriptions.

=head2 add

  $subs->add( %attr );

Add a subscription.  See L<Net::Object::Peer::Subscription> for the
supported attributes.

=head2 find

  my @subs = $subs->find( $coderef | %spec );

Returns a list of hashrefs containing attributes for subscriptions
which match the passed arguments.

A single argument must be a coderef; it will be invoked with a
L<Net::Peer::Subscription> object as an argument.  It should return
true if it matches, false otherwise.

If a hash is passed, its values are compared to the attributes of
subscriptions in the list.

=head2 remove

  @hashrefs = $subs->remove( $coderef | %spec );

Unsubscribe the matching subscriptions, remove them from the list of
subscriptions, and return hashrefs containing the subscriptions' event
names and peers.

=head1 AUTHOR

Diab Jerius <djerius@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2016 by Smithsonian Astrophysical Observatory.

This is free software, licensed under:

  The GNU General Public License, Version 3, June 2007

=cut

__END__

#pod =head1 DESCRIPTION
#pod
#pod A B<Net::Object::Peer::Subscriptions> object manages a collection
#pod of L<Net::Object::Peer::Subscriptions> objects.
