#!/usr/bin/perl
use strict;
use Event qw(loop unloop);
use Event::Watcher qw(R W T);

use IO::Socket;
use Tie::RefHash;

use encode_decode_ping;

my $port = 7676;
my $wait_for_accept_limit = 2*60*60;
my $ping_timeout = 30;

# Listen to port.
my $server = IO::Socket::INET->new(LocalPort => $port,
				   Listen    => 20,
				   Reuse     => 1)
  or die "Can't make server socket: $@\n";

# initialize acceptor
my $accept_watcher = Event->io(disc => 'accept',
			       fd=>$server,
			       poll => 'r',
			       prio => 4, # normal
			       cb => \&accept_handler,
			       max_cb_tm => 5,
			       timeout => $wait_for_accept_limit,
			       timeout_cb => \&too_long_waiting_on_accept);

# enter the main event loop
exit loop();
# end of main()

sub too_long_waiting_on_accept {
  print "no accept for $wait_for_accept_limit seconds; exiting\n";
  unloop();
}

sub ping_timeout_handler {
  my ($ping_event) = @_;
  my $ping_fd = $ping_event->w->fd;
  print "no ping for $ping_timeout seconds; closing connection\n";
  $ping_fd->close();
  $ping_event->w->stop();
}

sub accept_handler {
  my ($accept_event) = @_;
  my $accept_fd = $accept_event->w->fd;

  # accept the new connection: this is now the 
  # ping port (socket, file descriptor, ... 
  # whatever you want to call it)
  my $ping_fd = $accept_fd->accept();

  # initialize ping (read) event
  my $ping_event = Event->io(disc => "remote_name",
			     fd=>$ping_fd,
			     poll => 'r',
			     prio => 2,
			     cb => \&ping_handler,
			     timeout => $ping_timeout,
			     timeout_cb => \&ping_timeout_handler);

  @$ping_event{'ibuf','obuf'} = ('')x2;
  $ping_event->{readcb} = \&reply_to_ping;

  print $ping_fd "hello\n";
  print "got accept\n";
}

sub ping_handler {
  my ($e) = @_;
  my $w = $e->w;

  my  $got = $e->got;

  #print "got ping \"$got\"\n";
  #sleep(5);

  if ($e->got & T) {
    close $w->fd;
    $w->cancel;
    return;
  }
  if ($e->got & R) {
    my $buf='';
    my $num_bytes = sysread $w->fd, $buf, 8192;
    #print "num_bytes = $num_bytes\n";
    if (!$num_bytes) {
      print "EOF\n";
      close $w->fd;
      $w->cancel;
      return;
    }
    warn "'$w->desc' R[".sanitize($buf)."]\n"
      if $w->debug+$Event::DebugLevel >= 3;

    $w->{ibuf} .= $buf;
    $w->{obuf} .= $w->{readcb}->($e);
  }
} 

sub reply_to_ping {

  my ($e) = @_;
  my $w = $e->w;
  my $ret='';

  # print "ibuf = $w->{ibuf}\n";

  while ($w->{ibuf} =~ s/^(.*?)\n//) {
    print "received ", $1, "\n";
    $ret = decode_ping($1);
    syswrite($w->fd, 
	     $ret,
	     length($ret));  
  }
};
