use strict;
use Sys::Syscall ':epoll';
use Test::More;
use POSIX;
use IO::Socket::INET;
use Socket qw(PF_INET IPPROTO_TCP SOCK_STREAM);

my ($sysname, $nodename, $release, $version, $machine) = POSIX::uname();

if ($^O ne 'linux' || $release =~ /^2\.[01234]\b/) {
    non_linux_26();
}

plan tests => 20;
ok(Sys::Syscall::epoll_defined(), "have epoll");

my $epfd = epoll_create();
ok($epfd >= 0, "did epoll_create");

ok(EPOLLHUP && EPOLLIN && EPOLLOUT && EPOLLERR,     "epoll masks");
ok(EPOLL_CTL_ADD && EPOLL_CTL_DEL && EPOLL_CTL_MOD, "epoll_ctl constants");

my $udp_sock = IO::Socket::INET->new(PeerPort  => 9999,
				     PeerAddr  => inet_ntoa(INADDR_BROADCAST),
				     Proto     => 'udp',
				     LocalAddr => '127.0.0.1',
				     Broadcast => 1 )
    or die "Can't bind : $@\n";

my $tempfd = fileno($udp_sock);

is(epoll_ctl($epfd, EPOLL_CTL_ADD, $tempfd, EPOLLOUT), 0, "epoll_ctl udp socket EPOLLOUT");

my $events = [];
is(epoll_wait($epfd, 1, 500, $events), 1, "epoll_wait");
my $ev = $events->[0];
ok(ref $ev eq "ARRAY", "got an array in our event");
$ev ||= [];
is($ev->[0], $tempfd, "event is stdout");
is($ev->[1], EPOLLOUT, "udp socket is writable");

is(epoll_ctl($epfd, EPOLL_CTL_MOD, $tempfd, EPOLLIN), 0, "watch reads on udp socket");
my ($t1, $t2);
$t1 = time();
is(epoll_wait($epfd, 1, 1000, $events), 0, "get no events");
$t2 = time();
ok($t2 > $t1 && $t2 < ($t1 + 3), "took a second");

my $port = 60000;
my $ip   = '127.0.0.1';
my $listen = IO::Socket::INET->new(Listen    => 5,
                                   LocalAddr => $ip,
                                   ReuseAddr => 1,
                                   LocalPort => $port,
                                   Proto     => 'tcp');
my $listen2 = IO::Socket::INET->new(Listen    => 5,
                                    LocalAddr => $ip,
                                    ReuseAddr => 1,
                                    LocalPort => $port+1,
                                    Proto     => 'tcp');
ok($listen, "made temp listening socket");
ok(fileno($listen), "has fileno");

my ($sock, $sock2);
socket $sock, PF_INET, SOCK_STREAM, IPPROTO_TCP;
socket $sock2, PF_INET, SOCK_STREAM, IPPROTO_TCP;
IO::Handle::blocking($sock, 0);
IO::Handle::blocking($sock2, 0);
connect $sock, Socket::sockaddr_in($port, Socket::inet_aton($ip));
connect $sock2, Socket::sockaddr_in($port+1, Socket::inet_aton($ip));
select undef, undef, undef, 0.25;

my $lifd1 = fileno($listen);
my $lifd2 = fileno($listen2);

$epfd = epoll_create();
is(epoll_ctl($epfd, EPOLL_CTL_ADD, fileno($listen), EPOLLIN), 0, "epoll_ctl listen socket writable") or diag "reason: $!";
is(epoll_ctl($epfd, EPOLL_CTL_ADD, fileno($listen2), EPOLLIN), 0, "epoll_ctl listen2 socket writable") or diag "reason: $!";
is(epoll_wait($epfd, 2, 500, $events), 2, "epoll_wait") or diag("Got $events->[0][0] (listen=$lifd1, listen2=$lifd2)");
ok(($events->[0][0] == fileno($listen) && $events->[1][0] == fileno($listen2)) ||
   ($events->[1][0] == fileno($listen) && $events->[0][0] == fileno($listen2)), "got both");

is(epoll_ctl($epfd, EPOLL_CTL_DEL, fileno($listen), 0), 0, "epoll_ctl del stdout");
ok(epoll_ctl($epfd, EPOLL_CTL_MOD, 9999, 0), "epoll_ctl on bad fd");

sub non_linux_26 {
    plan skip_all => "test good only for Linux 2.6+";
    exit 0;
}

