#!/usr/bin/perl -w
use strict;
use Data::Dumper;
use diagnostics;

$Data::Dumper::Purity = 1;         # fill in the holes for eval
$Data::Dumper::Deepcopy = 1;       # avoid cross-refs

my %state;

my $debug = 0;
$SIG{USR1}=sub { 
    if ($debug) {
	$debug = 0;
    } else {
	$debug = 1;
    }
}; 

$SIG{PIPE}= sub{ debug "pipe broken: $!" };

my $IN, $OUT;
for my $file ( "./to-testd.fifo", "./from-testd.fifo") {
    my $fh = make_fifo( $file );
}

sub make_fifo{
    my ($file) = @_;
    
    unless (-p $file ) {
	unlink $file;
	system( "mkfifo", "-m600", $file )
	    && die "can't create fifo $file: $!";
    }
}

sub open_fifo{
    my ( $file ) = @_;

    open (my $fh, "< $file") || die "can't read from $file: $!";
   
    my $rin = '';
    vec($rin,fileno($fh),1) = 1;
    
    return ($fh, $rin);
}

open (NET_FH, "> $NET_FH") || die "can't write to $file: $!";
select((select(NET_FH), $|=1)[0]); # make the filehandle hot


##################################################
##################################################
##
##  here the functions start
##
##################################################
##################################################

sub process_input{
    my ($message) = @_;

    chomp ($message);
    syslog(LOG_DEBUG,"incoming command: %s",$message);
    #message is of the format 
    #"$action: $time $ipaddress $port $service $string"
    # time: arrival time (ADD) polling time (UP) meaningless (DEL)
    # ipaddress: serves for identifaction/index in the lists 
    # $port: tcp/ip port for reachng the client (ADD) meaningless (DEL)
    # $service: for the client and for timing (ADD), meaningless (UP, DEL)
    # $addonstring: Filename (ADD, DEL): makes sure we find the right mail
    #                 on bounce and warning, warning string for
    #                 special pager handling (UP)
    my ($action, $timestamp, $ipaddress, $port, $service, $addonstring)
	= split(/\s+/,$message);

    $action    =~ /([A-Z]{2,3}):/ or die "action currupted: $_ $!"; $action = $1;
    $timestamp =~ /\d{10}/ or die "arrival_time currupted: $_ $!";
    $ipaddress =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ or die "IP currupted: $_ $!";
    $port      =~ /\d{2,5}/ or die "port currupted: $_ $!";
    $service   =~ /[a-z,:]+/ or die "service currupted: $_ $!";
    $service   =~ s/:\d+//g; # fix the services field to not enclude numbers 
#    $filename  =~ /^\/var\/mail\/\d+\/Maildir\/.*\/\d{10}.*$/ or die "filename currupted: $_ $!";
    
    my @message=($timestamp,$ipaddress,$port,$service,$addonstring);

    $_ = $action;
  SWITCH: {
      /^ADD/ && do { add_message(@message);   last SWITCH; };
      /^DEL/ && do { del_message(@message);   last SWITCH; };
      /^UP/  && do { update_client(@message); last SWITCH; };
      die "unknown command: $_ $!";
  }    
}


sub dump_data{
    
    syslog(LOG_CRIT,"Dumpig Data");
    open (DUMP, "> /var/mail/queue-data-dump") || die "can't write to /var/mail/queue-data-dump: $!";
    my $dump= Dumper(%EventList);
    for (split('\n',$dump)){
	print DUMP "EventList:$_\n";
    }
    $dump= Dumper(%ClientList);
    for (split('\n',$dump)){
	print DUMP "ClientList:$_\n";
    }
    close DUMP;
    syslog(LOG_CRIT,"Data Dump finished.");

    $data_dump = 0;
}

#################################
##
##  main
##
#################################

my ($fifo_handle,$rin)=open_realworld_fifo;
my $nfound; 
my $timeleft;
my $rout;

while (1) {
    my $pause=calculate_next_PIT; #PIT =PointInTime
    # input: timeleft 0.86 nfound 1
    # timeout: timeleft 0 nfound 0
    if ($debug and $pause){
	syslog( LOG_DEBUG, "select -- pause %d sec till: %s", 
		$pause, scalar localtime( $pause + time));
    }
    ($nfound,$timeleft) =
	select($rout=$rin, undef, undef, $pause);

    die "impossibel select result: $nfound $timeleft $!" 
	if ( $nfound == 0 and $timeleft > 0  );

    if ( $nfound == 1 ) { # input
	my $buf = <$fifo_handle>;
	if (defined $buf){
	    process_input($buf); 
	    scheduler();
	}
    } elsif ($nfound == 0 and $timeleft == 0) { # timeout
	scheduler();
    }
    dump_data if $data_dump;
}
