package Lire::OldDlfAdapter;

use strict;

use base qw/ Lire::DlfConverter /;

use Carp;

use Fcntl;
use File::Basename qw/ basename /;
use Symbol;

use Lire::DlfSchema;
use Lire::Utils qw/ tempdir check_param /;
use Lire::DataTypes qw/ check_superservice /;
use File::Path qw/rmtree/;

=pod

=head1 NAME

Lire::OldDlfAdapter - Module which bridges with old-style DLF converters

=head1 SYNOPSIS

  my $converter = new Lire::OldDlfAdatper( "www",
                                           "/usr/libexec/lire/converters/combined2dlf" );

=head1 DESCRIPTION

This class can be used to integrate the script-based old-style DLF
converters to the new API.

=head2 new( $schema, $script )

The constructor takes the schema's supported by the script and the
path to the script which implements the DLF converter.

=cut

sub new {
    my ( $class, $schema, $script ) = @_;

    check_param( $schema, 'schema',
                 sub { Lire::DlfSchema->has_schema( $_[0] ); },
                 'invalid schema name' );
    check_param( $script, 'script', sub { -x $_[0] },
                 'script is not executable' );

    my ($name) = basename( $script );
    croak "invalid script name: $script (should be <service>2dlf)"
      unless $name =~ /^(\w+)2dlf$/;

    my $self = bless { '_name'   => $1,
                       '_script' => $script,
                       '_schema' => $schema,
                     }, ref $class || $class;
    return $self;
}

#------------------------------------------------------------------------
# Method name()
#
# Required by Lire::DlfConverter.
sub name {
    $_[0]{'_name'};
}

=pod

=head2 script()

Returns the path to the script which is used to create the DLF records

=cut

sub script {
    $_[0]{'_script'};
}

#------------------------------------------------------------------------
# Method title()
#
# Required by Lire::DlfConverter.
sub title {
    "$_[0]{'_name'} log file";
}

#------------------------------------------------------------------------
# Method description()
#
# Required by Lire::DlfConverter.
sub description {
    "<para>Adapter for old-style DLF converter $_[0]{'_script'}.</para>";
}

#------------------------------------------------------------------------
# Method schemas()
#
# Required by Lire::DlfConverter.
sub schemas {
    ( $_[0]{'_schema'} );
}


#------------------------------------------------------------------------
# Method handle_log_lines()
#
# Required by Lire::DlfConverter.
sub handle_log_lines { 1 }

#------------------------------------------------------------------------
# Method init_dlf_converter( $process )
#
# Required by Lire::DlfConverter.
sub init_dlf_converter {
    my ( $self, $process ) = @_;

    my $schema = Lire::DlfSchema::load_schema( $self->{'_schema'} );
    $self->{'_field_names'} = [];
    foreach my $f ( $schema->fields ) {
        push @{$self->{'_field_names'}}, $f->name;
    }

    $self->{'_dlffile'} = undef;
    $self->{'_logfile'} = undef;
    $self->{'_tmpdir'} = tempdir( $self->{'_name'} . "_XXXXXX" );
    open( $self->{'_dlffile'}, "+> $self->{'_tmpdir'}/$self->{'_name'}.dlf" )
      or die "can't open temporary dlf file: $!\n";
    open( $self->{'_logfile'}, "+> $self->{'_tmpdir'}/$self->{'_name'}.log" )
      or die "can't open temporary log file: $!\n";

    my ($read_fh, $write_fh) = ( gensym, gensym );
    pipe( $read_fh, $write_fh )
      or die "pipe failed: $!\n";
    $self->{'_convert_pid'} = fork;
    die "fork failed: $!\n" unless defined $self->{'_convert_pid'};
    if ( !$self->{'_convert_pid'} ) {
        close $write_fh;
        open( STDIN, "<&". fileno( $read_fh ) )
          or die "cannot redirect STDIN to pipe: $!\n";
        close $read_fh;
        open( STDOUT, ">&" . fileno( $self->{'_dlffile'} ) )
          or die "cannot redirect STDOUT to file: $!\n";
        close $self->{'_dlffile'};
        open( STDERR, ">&" . fileno( $self->{'_logfile'} ) )
          or die "cannot redirect STDERR to file:\n";
        close $self->{'_logfile'};
        exec( $self->{'_script'} )
          or die "exec $self->{'_script'} failed: $!\n";
    }
    close $read_fh;

    $self->{'_convert_fh'} = $write_fh;
}

#------------------------------------------------------------------------
# Method process_log_line( $process, $line )
#
# Required by Lire::DlfConverter.
sub process_log_line {
    my ( $self, $process, $line ) = @_;

    print {$self->{'_convert_fh'}} $line, "\n"
      or die "error while sending data to old dlf converter: $!\n";

    return;
}

#------------------------------------------------------------------------
# Method finish_conversion( $process )
#
# Required by Lire::DlfConverter.
sub finish_conversion {
    my ( $self, $process ) = @_;

    close $self->{'_convert_fh'}
      or die "closing pipe failed: $!\n";
    waitpid $self->{'_convert_pid'}, 0
      or die "waitpid failed: $!\n";
    my $status = $?;

    $self->_import_dlf( $process );
    $self->_import_errors( $process );

    die "old-style DLF converter exited with non-zero status: $?\n"
      if $status;

    rmtree( $self->{'_tmpdir'} );

    return;
}

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

    my $fh = $self->{'_dlffile'};
    seek $fh, 0, 0
      or die "can't rewind temporary DLF file: $!\n";

    my $field_count = @{$self->{'_field_names'}};
    my $line;
    while ( defined( $line = <$fh>) ) {
        chomp $line;
        my %r = ();
        my $i=0;
        foreach my $v ( split / /, $line ) {
            $r{$self->{'_field_names'}[$i++]} =
              $v eq 'LIRE_NOTAVAIL' ? undef : $v;
        }
        if ($i != $field_count ) {
            $process->error( "only $i fields when $field_count expected",
                             $line );
        } else {
            $process->write_dlf( $self->{'_schema'}, \%r );
        }
    }
}

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

    my $fh = $self->{'_logfile'};
    seek $fh, 0, 0
      or die "can't rewind temporary log file: $!\n";
    my $line;
    while (defined ( $line = <$fh> ) ) {
        chomp $line;
        my ($super,$service,$id,$prog,$level, $msg ) = split /\s+/, $line, 6;
        next unless $level =~ /^(crit|err|warning)$/;
        $process->error( $msg );
    }
}

# keep perl happy
1;

__END__

=pod

=head1 SEE ALSO

Lire::DlfConverterProcess(3pm), Lire::DlfStore(3pm), Lire::ImportJob(3pm),
Lire::PluginManager(3pm), Lire::DlfConverter(3pm)

=head1 AUTHOR

  Francis J. Lacoste <flacoste@logreport.org>

=head1 VERSION

$Id: OldDlfAdapter.pm,v 1.17 2006/07/23 13:16:29 vanbaal Exp $

=head1 COPYRIGHT

Copyright (C) 2002 Stichting LogReport Foundation LogReport@LogReport.org

This file is part of Lire.

Lire 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.

This program 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 this program (see COPYING); if not, check with
http://www.gnu.org/copyleft/gpl.html.

=cut
