#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket::INET;
use Getopt::Long qw(:config posix_default bundling);
use Net::SIP;
use Net::SIP::Util 'create_socket_to';
use Net::SIP::Debug;
use Time::HiRes qw(usleep gettimeofday tv_interval);
use threads;
use threads::shared;
use IO::Handle;
use Socket;    
use POSIX; 
use Getopt::Long;
###################################################
# Get options
###################################################

my ($twh,$registrar,$username,$password,$local_leg);
my ($debug,$hangup);
my $p=@ARGV;
usage() if ( @ARGV !=8 or !GetOptions(
    '-u=s' => \$username,
    '-p=s' => \$password,
    '-h=s' => \$registrar,
    '-d=i' => \$twh,
 ));

sub usage {
    print "Usage: $0 -u user -p passwd -h host -d duration\n";
    exit;
}
###################################################
#my $thr = threads->new(\&registration);
#$thr->detach;

$SIG{USR1} = \&safe_exit;
$SIG{INT} = \&safe_exit;
$SIG{QUIT} = \&safe_exit;
$SIG{TERM} = \&safe_exit;

my $switchboard = $$;
my $SRT = [gettimeofday];

my $cthr = threads->new(\&chrono);
$cthr->detach;

my @result; 
#for (my $j=0;$j<10;$j++){
#registration($j);
#}
my $idr = inet_aton($registrar); 
my $hst  = gethostbyaddr($idr, AF_INET);

print "\nREG SIP PING $hst ($registrar) during $twh s.";
while (1) {
my $pid = open FILS, "-|";
    if ($pid) {
        # le père
        my $rs=<FILS>;
        push @result,$rs;
        if ($rs) {
           print "\n200 ok, time: $rs ms.";
        }
        else {
           print "\nregistration timeout.";
        }
    }
    else {
        # le fils
        registration();
        exit 0;
    }
}

###################################################
# Signal Handlers
###################################################
sub safe_exit {
my($sig) = @_;
my $t = [gettimeofday];
my ($cmp,$succ,$min,$avg,$max,$pers,$perc)=(0,0,0,0,0,0,0);
foreach my  $v (@result) {
   if ($v > 0) {
      $succ++;
      $min=$v if (!$min);
      $max=$v if ($v>$max);
      $min=$v if ($v<$min && $v);
      $avg+=$v;
      }
   $cmp++;
   }
$avg=$avg/$succ if ($succ);
if ($succ==$cmp ){
   $pers="100"; 
   $perc="0";
   }
else {
$pers=ceil(($succ/$cmp)*100) if $cmp;
$perc=100-$pers;
}
if (!$cmp) {
$pers=0;
$perc=0;
}
$pers.='%';
$perc.='%';
print "\n--- $hst reg sip ping statistics ---\n";
printf "\n%d registrations transmitted, %d received(%s), %d loss(%s), time %0.2f ms",$cmp,$succ,$pers,$cmp - $succ,$perc,tv_interval($SRT, $t);
printf "\nmin/avg/max = %0.2f/%0.2f/%0.2f",$min,$avg,$max;
my $sdtr='';
foreach my  $v (@result) {
   $sdtr.= $v."/";
   }
print "\n--- \ntime list   = ".substr($sdtr,0,length($sdtr)-1)."\n";
exit;
}
###################################################
# Execution chronometer
###################################################
sub chrono {
while (1) {
my $t = [gettimeofday];
   if (tv_interval($SRT, $t) > $twh) {
      kill 'USR1',$switchboard;
      }
   else {
      usleep(500);
      }
   }
}
###################################################
# SIP Registration
###################################################
sub registration {
# create user agent
Net::SIP::Debug->level( $debug || 1 ) if defined $debug;
###################################################
# find local leg
###################################################
my ($local_host,$local_port);
if ( $local_leg ) {
        ($local_host,$local_port) = split( m/:/,$local_leg,2 );
} elsif ( ! $registrar ) {
        # if no proxy is given we need to find out
        # about the leg using the IP given from FROM
        ($local_host,$local_port) = $username =~m{\@([\w\-\.]+)(?::(\d+))?}
                or die "cannot find SIP domain in '$username'";
}

my $leg;
if ( $local_host ) {
        my $addr = gethostbyname( $local_host )
                || die "cannot get IP from SIP domain '$local_host'";
        $addr = inet_ntoa( $addr );

        $leg = IO::Socket::INET->new(
                Proto => 'udp',
                LocalAddr => $addr,
                LocalPort => $local_port || 5060,
        );

        # if no port given and port 5060 is already used try another one
        if ( !$leg && !$local_port ) {
                $leg = IO::Socket::INET->new(
                        Proto => 'udp',
                        LocalAddr => $addr,
                        LocalPort => 0
                ) || die "cannot create leg at $addr: $!";
        }

        $leg = Net::SIP::Leg->new( sock => $leg );
}


###################################################
# SIP code starts here
###################################################

# create necessary legs
# If I have an only outgoing proxy I could skip this step because constructor
# can make leg to outgoing_proxy itself
my @legs;
push @legs,$leg if $leg;
foreach my $addr ( $registrar,$registrar) {
        $addr || next;
        if ( ! grep { $_->can_deliver_to( $addr ) } @legs ) {
                my $sock = create_socket_to($addr) || die "cannot create socket to $addr";
                push @legs, Net::SIP::Leg->new( sock => $sock );
        }
}





my $ua = Net::SIP::Simple->new(
        from => $username,
        outgoing_proxy => $registrar,
        domain => 'sipX.youroute.net',
        expires => '100',
        legs => \@legs,
        $username ? ( auth => [ $username,$password ] ):(),
);
# optional registration
if ( $registrar && $registrar ne '-' ) {
        my $t0 = [gettimeofday];
        $ua->register( registrar => $registrar );
        my $t1 = [gettimeofday];
        if ($ua->error) {
          print "-1";
          }
       else {
          printf "%0.4f", tv_interval($t0, $t1);
          }
}
}
###################################################
# END
###################################################
