#
# 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: Infos.pm 3 2007-10-18 16:20:19Z guillaume $
#
# Class to report any usefull information
#

package A2P::Infos ;

use strict;
use A2P::Globals ;
use A2P::Globals qw( get_default_lockid );
use A2P::Syslog ;
use A2P::Init ;
use A2P::Init qw( ResetInit );

BEGIN {
    our $VERSION = sprintf "%s", q$Rev: 1185 $ =~ /(\d[0-9.]+)\s+/ ;
}
our $VERSION ;

sub new {
    my $class  = shift ;
    my $self = {
       LOCKID_LIST  => []
       } ;
    bless $self , $class ;
    &Debug("new ".__PACKAGE__." object v$VERSION created");
    return $self ;
}

my $default_lockid = &get_default_lockid() ;
my $config ;

sub _get_local_servicename {
    my $lockid = shift ;
    my $service = shift ;

    my @confs ;
    my @found = () ;

    # If one more argument, it's directly the local service name
    if (defined($service)) {
        @confs = ( '/etc/afp2print/' . $service . '.conf' ) ;

    } else {
        # Get all local configuration files
        @confs = glob( '/etc/afp2print/*.conf' ) ;
    }

    foreach my $conf (@confs) {
        my ( $name ) = $conf =~ m|^/etc/afp2print/([^/]+)\.conf$| ;
        # Skip other configurations
        next if ( $name =~ /^destid|documents|afpds2tex|stat|e-service/ );

        &Debug("Checking '$conf' configuration") if ($ADVANCED_DEBUGGING);

        # Initialize Init module
        &ResetInit ;
        &Init( "$A2P_SYS_CONF" , $conf );

        # Requested values must be specified here
        my @env = &Init(
            '$TEXSPOOL'       => '',
            '$AFPSPOOL'       => '',
            '$ERRORSPOOL'     => '',
            '$DONESPOOL'      => '',
            '$LOCKID'         => $default_lockid,
            '$SCAN_SPOOL'     => 0,
            '$KEEP_JOBSTATUS' => 0
            );
        my %env = () ;
        foreach my $env ( @env ) {
            $env =~ s|ENV\{(\w+)\}|env{$1}| ;
            eval $env ;
        }

        next unless ( $env{LOCKID} eq $lockid
            and $env{SCAN_SPOOL} and $env{KEEP_JOBSTATUS});

        &Debug("Service '$name' uses '$env{LOCKID}' LOCKID")
             if ($ADVANCED_DEBUGGING);

        # Keep config for other API
        $config = \%env ;

        push @found, $name ;
    }

    return @found ;
}

sub _recursive_serviceid_table_check {
    my $ref = shift ;
    my $depth = shift || 0 ;
    return &Error("Not a hash ref at depth $depth in services id list")
        unless (ref($ref) =~ /^HASH/);
    if ($depth<2) {
        map {
            unless(&_recursive_serviceid_table_check($ref->{$_},$depth+1)) {
            delete $ref->{$_} ;
            &Error("Key '$_' deleted at depth $depth in services id list");
            }
        } keys(%{$ref}) ;

    } elsif (!exists($ref->{serviceid}) or !defined($ref->{serviceid})
        or !$ref->{serviceid}) {
            return &Error("'serviceid' key not found");
    }
    return scalar(keys(%{$ref})) ;
}

sub get_lockid_list {
    return @{$_[0]->{LOCKID_LIST}} ;
}

sub get_serviceid_of {
    my $self = shift ;
    my $object = shift ;

    my $lockids = $self->{LOCKID} ;
    $lockids = $self->{LOCKID} = {} unless (defined($lockids));

    # Services table is transmitted by A2P::DB as hash to initialize our list
    if (ref($object) =~ /^HASH/) {
        if (&_recursive_serviceid_table_check($object)) {
            my @services = () ;
            $self->{LOCKID_ERROR} = 0 ;

            # Scan hash to get any (lockid,serviceid) pair
            foreach my $name (keys(%{$object})) {
                foreach my $lockid (keys(%{$object->{$name}})) {
                    my $serviceid =
                        exists($object->{$name}->{$lockid}->{serviceid})
                        ?
                        $object->{$name}->{$lockid}->{serviceid}
                        :
                        &Error("No serviceid found for '$name-$lockid'")
                        ;
                    #&Debug("Keeping serviceid $serviceid for '$name-$lockid'");
                    push @services, [ $lockid , $serviceid , $name ] ;
                }
            }

            # Scan each pair to get conflicting ones
            while (@services) {
                my ( $lockid, $serviceid, $name ) = @{ shift @services } ;
                my @match = () ;
                if (@match = grep { $_->[0] eq $lockid } @services) {
                    &Debug("Found services with same LOCKID for this server");

                    # 0. Still strip conflicting from list
                    @services = grep { $_->[0] ne $lockid } @services ;

                    # 1. Search available LOCKID in local configurations
                    my @localname = &_get_local_servicename($lockid);
                    if (!@localname) {
                        &Debug("No local service with $lockid LOCKID found, skipping it");
                        next ;

                    } elsif (@localname == 1 and $localname[0] eq $name) {
                        # This lockid is in fact the only one used today, so
                        # just remove the others
                        &Debug("Found '$lockid' is managed by service '$name'");

                    } elsif (@localname == 1) {
                        # Only local service really use this lockid but it should
                        # be in @match others list
                        my @realone = grep { $_->[2] eq $localname[0] } @match ;
                        &Debug("Not installed locally"),next unless (@realone);
                        &Debug("Found '$lockid' is managed by service '$localname[0]'");
                        &Warn("Found many local service '$name-$lockid', please update your confs")
                            if (@realone>1);
                        ( $lockid, $serviceid, $name ) = @{$realone[0]} ;

                    } else {
                        # Found many local running service with the same LOCKID, we need to
                        # say something is bad in configurations
                        &Warn("Bad server configuration found: ".
                            "many service seems to have the same LOCKID",
                            "So I will associating serviceid $serviceid to only service $name",
                            "You should check configuration files, and set different LOCKID for each one",
                            map { "'$_' service configuration is to check"} @localname
                            );
                    }
                }

                if ( ! defined($lockids->{$lockid}) or
                    $lockids->{$lockid} != $serviceid ) {
                    &Debug("Setting serviceid to $serviceid for lockid $lockid as service $name");
                    $lockids->{$lockid} = $serviceid ;
                }
            }

            # Update lockid list
            $self->{LOCKID_LIST} = [ keys(%{$lockids}) ] ;

            # And return ourself
            return $self ;

        } else {
            return &Error("No serviceid table found in '$object'");
        }

    }

    return (defined($lockids->{$object}) and $lockids->{$object}) ?
        $lockids->{$object} : 0 ;
}

sub get_servicename_of {
    my $self = shift ;
    my @name = &_get_local_servicename(@_) ;
    my $name = shift @name ;
    return '' unless ( defined($name) and $name );
    #&Debug("Servicename of '@_' is '$name'");
    return $name ;
}

sub get_afpspool_of {
    my $self = shift ;
    my $lockid = shift ;
    my $service = shift || 'afpds2tex' ;
    my @name = &_get_local_servicename($lockid,$service) ;
    my $name = shift @name ;
    return &Error("No conf found for $service service (Got '$name')")
        unless ( $name eq $service );
    #&Debug("Servicename of '@_' is '$name'");
    return exists($config->{'AFPSPOOL'}) ? $config->{'AFPSPOOL'} :
        &Error("AFPSPOOL not found for $service service") ;
}

sub get_spools_of {
    my $self = shift ;
    my $lockid = shift ;
    my $service = shift || 'afpds2tex' ;
    my @name = &_get_local_servicename($lockid,$service) ;
    my $name = shift @name ;
    return &Error("No conf found for $service service (Got '$name')")
        unless ( $name eq $service );
    #&Debug("Servicename of '@_' is '$name'");
    return map { exists($config->{$_}) ? $config->{$_} : '' }
        qw( DONESPOOL ERRORSPOOL ) ;
}

&Debug("Module " . __PACKAGE__ . " v$VERSION loaded");

1;

