#!/bin/env perl
###
### pstampparser_run.pl
###     Run the request parser for a given request id
### This script should be called request_parser.pl since it handles more than postage
### stamp requests
###

use warnings;
use strict;

use Sys::Hostname;
use Getopt::Long qw( GetOptions );
use File::Basename qw( basename dirname);
use POSIX qw( strftime );
use Carp;
use IPC::Cmd 0.36 qw( can_run run );

use PS::IPP::Metadata::Config;
use PS::IPP::Metadata::Stats;
use PS::IPP::Metadata::List qw( parse_md_list );

use PS::IPP::Config qw( :standard );

my $req_id;
my $uri;
my $redirect_output;
my $product;
my $verbose;
my $dbname;
my $dbserver;

GetOptions(
    'req_id=s'          =>  \$req_id,
    'uri=s'             =>  \$uri,
    'product=s'         =>  \$product,
    'redirect-output'   =>  \$redirect_output,
    'verbose'           =>  \$verbose,
    'dbname=s'          =>  \$dbname,
    'dbserver=s'         =>  \$dbserver,
);

if ($verbose) {
    my $host = hostname();
    print "\n\n";
    print "Starting script $0 on $host\n\n";
}

my $missing_tools;

my $pstamptool  = can_run('pstamptool')  or (warn "Can't find pstamptool"  and $missing_tools = 1);
my $pstampparse = can_run('pstampparse.pl') or (warn "Can't find pstampparse.pl" and $missing_tools = 1);
my $dqueryparse = can_run('dqueryparse.pl') or (warn "Can't find dqueryparse.pl" and $missing_tools = 1);
my $dsget = can_run('dsget') or (warn "Can't find dsget" and $missing_tools = 1);

if ($missing_tools) {
    warn("Can't find required tools.");
    exit ($PS_EXIT_CONFIG_ERROR);
}


my_die("--req_id --uri --product are required", $req_id, $PS_EXIT_CONFIG_ERROR)
    if !defined($req_id) or
       !defined($uri) or
       !defined($product);

my $ipprc = PS::IPP::Config->new(); # IPP Configuration

my $outputDataStoreRoot = metadataLookupStr($ipprc->{_siteConfig}, 'DATA_STORE_ROOT');
exit ($PS_EXIT_CONFIG_ERROR) unless defined $outputDataStoreRoot; # lookup failure outputs a message
my $defaultOutputRoot = $outputDataStoreRoot;

my $pstamp_workdir = metadataLookupStr($ipprc->{_siteConfig}, 'PSTAMP_WORKDIR');
exit ($PS_EXIT_CONFIG_ERROR) unless defined $pstamp_workdir; # lookup failure outputs a message

if (!$dbserver) {
    $dbserver =  metadataLookupStr($ipprc->{_siteConfig}, 'PS_DBSERVER');
}

# workdir is where all of the files generated for this request are placed
# NOTE: this location needs to be kept in sync with the web interface ( request.php )
my $datestr = strftime "%Y%m%d", gmtime;
my $datedir = "$pstamp_workdir/$datestr";
if (! -e $datedir ) {
    mkdir $datedir or my_die( "failed to create working directory $datedir for request id $req_id", $req_id,
        $PS_EXIT_CONFIG_ERROR);
}

my $workdir = "$datedir/$req_id";
if (! -e $workdir ) {
    mkdir $workdir or my_die("failed to create working directory $workdir for request id $req_id", $req_id,
        $PS_EXIT_CONFIG_ERROR);
}

if ($redirect_output) {
    my $logDest = "$workdir/psparse.$req_id.log";
    $ipprc->redirect_output($logDest);
}

my $defaultDSProduct = metadataLookupStr($ipprc->{_siteConfig}, 'PSTAMP_DATA_STORE_PRODUCT');
exit ($PS_EXIT_CONFIG_ERROR) unless defined $defaultDSProduct;
    
my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files


my $fn = basename($uri);
my $new_uri = "$workdir/$fn";
if ($uri =~ /^http:/) {
    # if the uri is an http uri download the file 
    my $command = "$dsget --uri $uri --filename $new_uri";
    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
        run(command => $command, verbose => $verbose);
    unless ($success) {
        my_die("Unable to perform $command error code: $error_code", $req_id, $error_code >> 8);
    }
} elsif ($uri ne $new_uri) {
    # put a link to the file into the workdir
    if (-e $new_uri) {
        unlink $new_uri or my_die("failed to unlink $new_uri", $req_id, $PS_EXIT_UNKNOWN_ERROR);
    }
    if (! symlink $uri, $new_uri) {
        my_die ("failed to link request file $uri to workdir $workdir", $req_id, $PS_EXIT_UNKNOWN_ERROR);
    }
}
$uri = $new_uri;

my_die("request file $uri not found", $req_id, $PS_EXIT_UNKNOWN_ERROR) if ! -e $uri;

#  if product was not defined (in database), use the default
if ($product eq "NULL") {
    $product = $defaultDSProduct;
}

my $parse_cmd;
my $request_type;
my $reqType;    # for the database

if (-r $uri) {
    # run the appropriate parse command to parse the queue the jobs for this request
    # first check the extension header to find the EXTNAME
    $request_type = find_request_type($uri);

    if ($request_type) {
        print STDERR "request_type for $req_id is $request_type\n" if $verbose;
        if ($request_type eq "PS1_PS_REQUEST") {
            $reqType = 'pstamp';
            $parse_cmd = $pstampparse;
        } elsif ($request_type eq "MOPS_DETECTABILITY_QUERY") {
            $reqType = 'dquery';
            $parse_cmd = $dqueryparse;
        } else {
            print STDERR "Unknown request type $request_type found in $uri";
        }
    } else {
        print STDERR "No EXTNAME found keyword in $uri";
    }
} else {
    if (-e $uri) {
        print STDERR "Request file $uri is not readable";
    } else {
        print STDERR "Request file $uri does not exist";
    }
}

if (!$parse_cmd) {
    # can't go any farther, set fault and set request state to run 
    # request_finish.pl will clean up, perhaps notifiying the operator of the input data store
    # that they sent us a request file that we don't understand

    my $command = "$pstamptool -updatereq -req_id $req_id -state run";
    $command   .= " -reqType unknown";
    $command   .= " -fault $PS_EXIT_DATA_ERROR";
    $command   .= " -dbname $dbname" if $dbname;
    $command   .= " -dbserver $dbserver" if $dbserver;
    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
        run(command => $command, verbose => $verbose);
    unless ($success) {
        die("Unable to perform $command error code: $error_code");
    }
    exit $PS_EXIT_DATA_ERROR;
}

$parse_cmd .= " --mode queue_job --req_id $req_id --product $product --out_dir $workdir --file $uri";
$parse_cmd .= " --dbname $dbname" if $dbname;
$parse_cmd .= " --dbserver $dbserver" if $dbserver;
$parse_cmd .= " --verbose" if $verbose;

my $newState = "run";
my $fault;
{
    my $error_file_name = "$workdir/parse_error.txt";
    # get rid of any error file from previous attempt to parse this request
    unlink $error_file_name if (-e $error_file_name);

    # Run the parser

    my $command = "$parse_cmd";
    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
        run(command => $command, verbose => $verbose);

    # save the contents of stderr (if any) to a file. This is relevant if
    # the file was parseable but one or more of the rows in the request file generated an error
    my $errbuf = join "", @$stderr_buf;
    if ($errbuf) {
        if (!open OUT, ">$error_file_name") {
            print STDERR ("unable to open parse_error file $error_file_name");
        } else {
            print OUT "$errbuf";
            close(OUT);
        }
        print STDERR $errbuf if $verbose;
    }

    unless ($success) {
        $fault = $error_code >> 8;
        $newState = "stop";
    }
}

#
# update the state of this request from 'new' to 'run' and set the uri to the downloaded location
#
{
    my $command = "$pstamptool -updatereq -req_id $req_id -state $newState";
    $command   .= " -reqType $reqType" if $reqType;
    $command   .= " -uri $new_uri" if $new_uri;
    $command   .= " -fault $fault" if $fault;
    $command   .= " -dbname $dbname" if $dbname;
    $command   .= " -dbserver $dbserver" if $dbserver;
    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
        run(command => $command, verbose => $verbose);
    unless ($success) {
        die("Unable to perform $command error code: $error_code");
    }
}

# Note: We do not return $fault here. If there was a fatal error we've already exited.
# If we got a fault it's due to bad input from the user we've set things up for this to be
# handled by the task pstamp.request.finish

exit 0;


sub find_request_type {
    # find the EXTNAME in the input fits table
    my $file_name = shift;
    my $out = `echo $file_name | fields -x 0 EXTNAME`;

    if ($out) {
        # output from fields is filename value
        my ($dummy, $extname) = split " ", $out;

        return $extname;
    } else {
        return undef;
    }
}

sub my_die {
    my $msg = shift;
    my $req_id = shift;
    my $fault = shift;

    carp($msg);

    my $command = "$pstamptool -updatereq -req_id $req_id  -fault $fault";
    $command   .= " -dbname $dbname" if $dbname;
    $command   .= " -dbserver $dbserver" if $dbserver;
    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
        run(command => $command, verbose => $verbose);
    unless ($success) {
        die("Unable to perform $command error code: $error_code");
    }
    exit $fault;
}
