The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package PocketIO::Handle;

use strict;
use warnings;

use AnyEvent::Handle;

use constant DEBUG => $ENV{POCKETIO_HANDLE_DEBUG};

sub new {
    my $class = shift;
    my (%params) = @_;

    my $fh = delete $params{fh};

    my $self = {handle => AnyEvent::Handle->new(fh => $fh), %params};
    bless $self, $class;

    $self->{heartbeat_timeout} ||= 10;

    $fh->autoflush;

    $self->{handle}->no_delay(1);
    $self->{handle}->on_eof(sub   { warn "Unhandled handle eof" });
    $self->{handle}->on_error(sub { warn "Unhandled handle error: $_[2]" });

    # This is needed for the correct EOF handling
    $self->{handle}->on_read(sub { });

    return $self;
}

sub fh { $_[0]->{handle}->fh }

sub on_heartbeat {
    my $self = shift;
    my ($cb) = @_;

    $self->{handle}->wtimeout($self->{heartbeat_timeout});
    $self->{handle}->on_wtimeout($cb);

    return $self;
}

sub on_read {
    my $self = shift;
    my ($cb) = @_;

    $self->{handle}->on_read(
        sub {
            my $handle = shift;

            $handle->push_read(
                sub {
                    $cb->($self, $_[0]->rbuf);
                }
            );
        }
    );

    return $self;
}

sub on_eof {
    my $self = shift;
    my ($cb) = @_;

    $self->{handle}->on_eof(
        sub {
            $cb->($self);
        }
    );

    return $self;
}

sub on_error {
    my $self = shift;
    my ($cb) = @_;

    $self->{handle}->on_error(
        sub {
            $cb->($self);
        }
    );

    return $self;
}

sub write {
    my $self = shift;
    my ($chunk, $cb) = @_;

    my $handle = $self->{handle};
    return $self unless $handle && $handle->fh;

    $handle->push_write($chunk);

    if ($cb) {
        $handle->on_drain(
            sub {
                my $handle = shift;

                $handle->on_drain(undef);

                $cb->($self);
            }
        );
    }

    return $self;
}

sub close {
    my $self = shift;

    my $handle = delete $self->{handle};
    return $self unless $handle;

    $handle->wtimeout(0);

    $handle->on_drain;
    $handle->on_error;

    $handle->on_drain(
        sub {
            if ($_[0]->fh) {
                shutdown $_[0]->fh, 1;
                close $handle->fh;
            }

            $_[0]->destroy;
            undef $handle;
        }
    );

    return $self;
}

1;
__END__

=head1 NAME

PocketIO::Handle - Handle

=head1 DESCRIPTION

L<PocketIO::Handle> is a wrapper on top of
L<AnyEvent::Handle>.

=head1 METHODS

=head2 C<new>

=head2 C<fh>

=head2 C<on_heartbeat>

=head2 C<on_read>

=head2 C<on_eof>

=head2 C<on_error>

=head2 C<write>

=head2 C<close>

=head1 SEE ALSO

L<AnyEvent::Handle>

=cut