package SlowReqWorker;

use CWorker;

our( @ISA );
@ISA = qw(CWorker);


sub new {
    my ($this, $info, $iohandle, $requester ) = @_;
    my  $class = ref($this) || $this;
    my  $self = {};
    bless($self, $class);

    $self->{"SRQ.info"} = $info;
    $self->{"SRQ.env"} = $requester->{"env"};
    $self->{"SRQ.requester"} = $requester;
    $self->{"SRQ.iohandle"} = $iohandle;
    $self->{"SRQ.writebuff"} = "";
    $self->{"SRQ.readbuff"} = "";
    $self->{"SRQ.timeout"} = $requester->timeout();
    $self->{"SRQ.start"} = time;
    $self->initialize();
    $self->criticalness(2);
    return $self;
}


sub startup {
    my( $self ) = @_;

    $self->{"SRQ.requester"}->do( $self );
    $self->{"filenumber"} = $self->{"SRQ.iohandle"}->fileno();
    $self->{"read"} = 1;
    $self->{"write"} = ($self->{"SRQ.writebuff"}) && 1;
    $self->{"timeout"} = $self->{"SRQ.start"} + $self->{"SRQ.timeout"};
    $self->{"SRQ.started"} = 1;
}


sub write {
    my( $self, $text ) = @_;

    $self->{"SRQ.writebuff"} .= $text;
}


sub read {
    my( $self, $size ) = @_;

    if( $size > length($self->{"SRQ.readbuff"}) ) {
	$size = length($self->{"SRQ.readbuff"});
    }
    my $text = substr($self->{"SRQ.readbuff"},0,$size);
    $self->{"SRQ.readbuff"} = substr($self->{"SRQ.readbuff"},$size);
    return $text;
}


sub readln {
    my( $self ) = @_;

    my $eol = index( $self->{"SRQ.readbuff"}, "\n" );
    if( $eol >= 0 ) {
        my $line = substr($self->{"SRQ.readbuff"},0,$eol+1);
	$self->{"SRQ.readbuff"} = substr($self->{"SRQ.readbuff"},$eol+1);
	return $line;
    }
    return "";
}



sub sockclose {
    my( $self ) = @_;

    close( $self->{"SRQ.iohandle"} );
    $self->finish();
}


sub status {
    my( $self, $code, $text ) = @_;

    print "  SlowReqWorker: $code $text\n" if( $BigSister::common::dl>5 );
    if( $code < 3 ) {
        $self->{"SRQ.requester"}->error( $text );
    }
    $self->sockclose();
}



sub do {
    my( $self ) = @_;
    unless( $self->{"SRQ.started"} ) {
	$self->startup();
	return;
    }

    my $mask = '';
    vec( $mask, $self->{"SRQ.iohandle"}->fileno(), 1 ) = 1;

    print "  I/O polling\n" if( $BigSister::common::dl>6 );
    my $timedout = $self->{"timeout"} <= time;
    my $wmask = $mask;
    my $write_ready = select( undef, $wmask, undef, 0 ) >0 ; 
    my $rmask = $mask;
    my $read_ready = select( $rmask, undef, undef, 0 ) >0 ; 
    my $exception = select( undef, undef, $mask, 0 ) >0 ; 
    if( $self->{"SRQ.writebuff"} ) {
	if( ! $write_ready ) {
	    $self->status( 0, $timedout ? "TIMED OUT" : "CONNECT FAILED" );
	    return;
	}
	my $len = syswrite( $self->{"SRQ.iohandle"}, $self->{"SRQ.writebuff"} );
	unless( $len ) {
	    $self->status( 0, ($self->{"SRQ.connected"})?"SEND FAILED":"CONNECT FAILED" );
	    return;
	}
	$self->{"SRQ.connected"} = 1;
	$self->{"SRQ.writebuff"} = substr( $self->{"SRQ.writebuff"}, $len );
	$self->{"read"} = 1;
    }
    if( $read_ready ) {
	my $size = sysread( $self->{"SRQ.iohandle"}, $self->{"SRQ.readbuff"}, 4096, length( $self->{"SRQ.readbuff"} ) );
	unless( $size ) {
	    print "  SlowReq error: $!\n" if( $BigSister::common::dl > 4 );
	    $self->status( 0, ($self->{"SRQ.connected"})?"CHAT FAILED":"CONNECT FAILED" );
	    return;
	}
	if( length( $self->{"SRQ.readbuff"} ) > 40000 ) {
	    $self->status( 1, "excessive data" );
	    return;
	}
    }
    $self->{"SRQ.requester"}->do( $self );
    $self->{"write"} = ($self->{"SRQ.writebuff"}) && 1;
}



sub timed_out {
    my( $self ) = @_;

    $self->status( 0, "TIMED OUT" );
}




package Requester::SlowRequester;

use strict;
use CWorker;
use FileHandle;
use Socket;
use Requester::Requester;

our( @ISA );
@ISA = qw(Requester::Requester);

my $tcp_proto = (getprotobyname('tcp'))[2];


sub TCP_port {
    my( $self, $port ) = @_;

    $self->{"SlowRequester.port"} = $port if( $port );
    return( $self->{"SlowRequester.port"} );
}



sub command {
    my( $self, $command ) = @_;

    $self->{"SlowRequester.command"} = $command if( $command );
    return( $self->{"SlowRequester.command"} );
}



sub timeout {
    my( $self, $timeout ) = @_;

    $self->{"SlowRequester.timeout"} = $timeout if( $timeout );
    return( $timeout || 20 );
}


sub getinfo {
    my( $self, $gets, $indexes ) = @_;

    my $iohandle = $self->startup( $gets, $indexes );
    return undef unless( $iohandle );
    my $info = {};
    my $tester = new SlowReqWorker( $info, $iohandle, $self );
    $tester->{"Test.info"} = $info;
    $self->setup( $tester, $gets, $indexes );
    return( $tester );
}


# ========================================================

# open a connection / start a command / whatever
# and return a file handle
sub startup {
    my( $self, $gets, $indexes ) = @_;
    my $fh = new FileHandle();
    if( $self->command() ) {
        open( $fh, $self->command()."|" );
	return( $fh );
    }
    if( $self->TCP_port() ) {
        my $port = $self->TCP_port();
	my $nport = (getservbyname($port, 'tcp'))[2];
	if( ($port =~ /^\d+$/) && ! defined $nport ) {
	    $nport = $port;
	}
	my $ip = inet_aton( $self->{"env"}->{"nodeinstance"}->{"host"} );
	unless( $ip && $nport
            && socket( $fh, &PF_INET(), &SOCK_STREAM(), $tcp_proto ) ) {
	    print "  SlowReq TCP configuration mistake\n" if( $BigSister::common::dl>4 );
	    return undef;
	}

	print "  SlowReq TCP connecting to ".(inet_ntoa($ip)).":$nport\n" if( $BigSister::common::dl>5 );
	my $saddr = sockaddr_in( $nport, $ip );
	my $isnonblock = Platform::socknonblock( $fh );
	my $ok = 0;
	eval {
	    # if socket is not in non-blocking mode we will (try) to
	    # timeout using alarm()
	    # otherwise connect() will return immediately
	    $SIG{'ALRM'} = sub { die; };
	    print "  SlowReq TCP connecting ...\n" if( $BigSister::common::dl>5 );
	    Platform::bsalarm( $self->timeout() );
	    unless( connect( $fh, $saddr ) ) {
		die unless( $isnonblock );
	    }
	    $ok = 1;
	    Platform::bsalarm( 0 );
	};
	$SIG{'ALRM'} = "IGNORE";
	return( $fh ) if( $ok );
	close $fh;
    }
    return undef;
}



# called immediately before SlowReqWorker takes over
# setup() may copy any volatile data (like "env") over into the $srq object
sub setup {
    my( $self, $srq, $gets, $indexes ) = @_;

    $srq->{"SlowRequester.env"} = $self->{"env"};
}


# do one step - read data via $srq->read(), $srq->readln() or write data via
# $srq->write(), call $srq->status() if finished
sub do {
   my( $self, $srq ) = @_;
   # the default is to do nothing
   $srq->status( 0, "nothing to do" );
}



# called if an error condition (e.g. lost connection) is detected
sub error {
    my( $self, $srq ) = @_;
}


1;

