package Mercury::PushPull;
# ABSTRACT: Push/pull message pattern
$Mercury::PushPull::VERSION = '0.006';

use Mojo::Base 'Mojo';


has 'topic';


has pullers => sub { [] };


has pushers => sub { [] };


has current_puller_index => sub { 0 };


sub add_puller {
    my ( $self, $c ) = @_;
    $c->on( finish => sub {
        my ( $c ) = @_;
        $self->remove_puller( $c );
    } );
    push @{ $self->pullers }, $c;
    return;
}


sub add_pusher {
    my ( $self, $c ) = @_;
    $c->on( message => sub {
        my ( $c, $msg ) = @_;
        $self->send_message( $msg );
    } );
    $c->on( finish => sub {
        my ( $c ) = @_;
        $self->remove_pusher( $c );
    } );
    push @{ $self->pushers }, $c;
    return;
}


sub send_message {
    my ( $self, $msg ) = @_;
    my $i = $self->current_puller_index;
    my @pullers = @{ $self->pullers };
    $pullers[ $i ]->send( $msg );
    $self->current_puller_index( ( $i + 1 ) % @pullers );
    return;
}


sub remove_puller {
    my ( $self, $c ) = @_;
    my @pullers = @{ $self->pullers };
    for my $i ( 0.. $#pullers ) {
        if ( $pullers[$i] eq $c ) {
            splice @{ $self->pullers }, $i, 1;
            my $current_puller_index = $self->current_puller_index;
            if ( $i > 0 && $current_puller_index >= $i ) {
                $self->current_puller_index( $current_puller_index - 1 );
            }
            return;
        }
    }
}


sub remove_pusher {
    my ( $self, $c ) = @_;
    my @pushers = @{ $self->pushers };
    for my $i ( 0.. $#pushers ) {
        if ( $pushers[$i] eq $c ) {
            splice @pushers, $i, 1;
            return;
        }
    }
}

1;

__END__

=pod

=head1 NAME

Mercury::PushPull - Push/pull message pattern

=head1 VERSION

version 0.006

=head1 SYNOPSIS

=head1 DESCRIPTION

=head1 ATTRIBUTES

=head2 topic

This object's topic, for accounting purposes.

=head2 pullers

Connected websockets ready to receive messages.

=head2 pushers

Connected websockets who will be pushing messages.

=head2 current_puller_index

The puller we will use to send the next message from a pusher.

=head1 METHODS

=head2 add_puller

    $pat->add_puller( $c );

Add a puller to this broker. Pullers are given messages in a round-robin, one
at a time, by pushers.

=head2 add_pusher

    $pat->add_pusher( $c );

Add a pusher to this broker. Pushers send messages to be processed by pullers.

=head2 send_message

    $pat->send_message( $msg );

Send the given message to the next puller in line.

=head2 remove_puller

    $pat->remove_puller( $c );

Remove a puller from the list. Called automatically when the puller socket
is closed.

=head2 remove_pusher

    $pat->remove_pusher( $c );

Remove a pusher from the list. Called automatically when the pusher socket
is closed.

=head1 AUTHOR

Doug Bell <preaction@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by Doug Bell.

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
