#
# Copyright (c) 2004-2007 - Consultas, PKG.fr
#
# This file is part of A2P.
#
# A2P is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# A2P is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with A2P; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
# $Id: Client.pm 3 2007-10-18 16:20:19Z guillaume $
#
# WARN SIGALRM is used to check time out,
# so remember it can override a client definition

package A2P::Client;

use strict ;
use Socket ;
use IO::Socket ;
use Time::HiRes  qw(ualarm gettimeofday tv_interval);
use A2P::Globals ;
use A2P::Syslog ;
use A2P::Com   qw( IsCom GetCom comJOB comREQ comCOM comDONE comASK comUPD GetTmpFile comTEST );
use A2P::Tools qw( ShortID );

BEGIN {
    use Exporter () ;
    our ( $VERSION , @ISA , @EXPORT );
    $VERSION = sprintf "%s", q$Rev: 415 $ =~ /(\d[0-9.]+)\s+/ ;
    @ISA = qw(Exporter);
    @EXPORT = qw(&TellListener &DoJob);
}
our $VERSION ;

# Set OUTPUT_AUTOFLUSH
$| = 1 ;

# Last argument to TellListener can be a sub callback
sub TellListener {
    my $which = shift ;
    $LISTENER = &GetTmpFile( $which . '-listener' );
    my $callback = pop if ( ref($_[$#_]) =~ /^CODE$/ );
    my @Default  = ( "NO RESPONSE" );
    my @Response = ();
    my $timer    = [ &gettimeofday() ] ;
    my $shutdown = 0 ;
    my $response = 0 ;

    return ( "SERVICE NOT AVAILABLE" ) if ( ! -S $LISTENER );
    return ( "NOTHING TO TELL" ) unless @_ ;

    $SIG{ALRM} = sub {
        # Check my time out
        return unless ( &tv_interval($timer) > $CLIENT_TIMEOUT );
        push @Default , "TIME OUT" ;
        $shutdown ++ ;
        shutdown SOCK , 2 ;
    };

    # Use ualarm to be advertize to check time-out
    &ualarm( 1000000 , 1000000 );

    socket(SOCK, PF_UNIX, SOCK_STREAM, 0)
        or die "Can't open a socket: $!" ;

    SOCK->autoflush(1);

    connect(SOCK, pack_sockaddr_un($LISTENER))
        or die "Can't connect to $LISTENER socket: $!" ;

    my $Question = &GetCom( comREQ , TODO , &GetCom( comASK , @_ ));
    &Debug("Asking '" . $Question . "' to $which service listener");

    local $\ = "\n" ;

    # Talk now with service
    if ( print SOCK $Question ) {
        while ( my $line = <SOCK> ) {
            chomp $line ;
            next unless $line ;

            my @content = &IsCom( comCOM , $line );
            my $info = @content ? $content[1] : $line ;

            # Update my time-out timer
            $timer = [ &gettimeofday() ] ;

            # Will leave loop if server answer COM is DONE, but keeping info
            if ( @content = &IsCom( comDONE , $info ) ) {
                $info = $content[1] ;

            } elsif ( $info =~ /^\d+$/ and $info == comTEST ) {
                print SOCK &GetCom( comCOM , $$ , comDONE );
                next ;
            }

            # Call back to client if requested
            defined($callback) ? &$callback($info) : push @Response, $info ;
            $response ++ ;

            last if ( @content and $content[0] =~ /^done$/ );
        }
    }

    &ualarm(0);

    @Response = @Default unless $response ;

    map {
        &Debug("Listener response: '" . $_ . "'")
    } @Response ;

    shutdown SOCK , 2
        unless $shutdown ;

    return @Response ;
}

sub JustOutPut {
    local $\ = "\n" ;
    print @_ ;
}

sub DoJob {
    my $which = shift ;
    my $file  = shift ;

    print "\nStarting job with [$which] service and '$file' file...\n";

    # Find an suitable Id for the job
    my ( $jid ) = $file =~ m|.*/(.+)(\.\w+)?$| ;
    ( $jid ) = $file =~ m|/?(\w+)(\.\w+)+$| unless (defined($jid));
    $jid = "JOB" . &ShortID(5) unless (defined($jid));

    if ( $which and $file and $jid ) {
        print "A2P JobID = $jid\n" ;
        &TellListener( $which,
            JobManager => &GetCom( comJOB , $jid => $file ), \&JustOutPut );

    } else {
        print "But it's impossible when an argument is missing\n" ;
        print "Tell me which service to ask as first argument\n" unless $which ;
        print "Tell me which file to compute as second argument\n" unless $file ;
        print "I can't compute Job ID with '$file'\n" unless $jid ;
    }
}

sub DoUpdate {
    my $service  = shift ;
    my $module   = shift ;
    my $varname  = shift ;
    my $value    = shift ;

    #print "\nUpdating value $varname on [$which-$module] service...\n";

    if ( $service and $module and $varname and defined($value) ) {
        &TellListener( $service,
            $module => &GetCom( comUPD , $varname => $value ), \&JustOutPut );

    } else {
        print "But it's impossible when an argument is missing\n" ;
        #print "Tell me which service to ask as first argument\n" unless $which ;
        #print "Tell me which file to compute as second argument\n" unless $file ;
        #print "I can't compute Job ID with '$file'\n" unless $jid ;
    }
}

&Debug("Module " . __PACKAGE__ . " v$VERSION loaded") unless ( $0 =~ /cgi/ );

1;
