#!/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 );

my $verbose;
my $dbname;
my $req_id;

GetOptions(
    'verbose'   =>  \$verbose,
    'dbname=s'  =>  \$dbname,
    'req_id=s'  =>  \$req_id,
);

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

die "req_id is required" if !defined($req_id);

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($PS_EXIT_SUCCESS
		       $PS_EXIT_UNKNOWN_ERROR
		       $PS_EXIT_SYS_ERROR
		       $PS_EXIT_CONFIG_ERROR
		       $PS_EXIT_PROG_ERROR
		       $PS_EXIT_DATA_ERROR
		       $PS_EXIT_TIMEOUT_ERROR
		       metadataLookupStr
		       metadataLookupBool
		       caturi
		       );

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

# workdir is where we download request files to and place any error output from the parser
# NOTE: this location needs to be kept in sync with the web interface ( request.php )
my $workdir = "$pstamp_workdir/$req_id";

if (! -e $workdir ) {
    mkdir $workdir or die "failed to create working directory $workdir for request id $req_id";
}

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

my $psrequest;
#Look up the uri for the given request
{
    my $command = "$pstamptool -pendingreq -req_id $req_id";
    $command .= " -dbname $dbname" if $dbname;
    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");
    }

    if (@$stdout_buf == 0) {
        print STDERR "pending pstamp request id $req_id not found\n";
        exit 1;
    }
    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
        die("Unable to parse metdata config doc");

    my $requests = parse_md_list($metadata);

    my $num = @$requests;
    die "unexpected number of requests $num found" if $num > 1;

    $psrequest = $requests->[0];
}

if (!$psrequest) {
    print STDERR "pending postage stamp request $req_id not found\n";
    exit 1;
}

my $uri = $psrequest->{uri};

# if the uri is from a data store, download it 

my $ds_id = $psrequest->{ds_id};

my $new_uri;
if ($ds_id && ($uri =~ /^http:/)) {
    {
        $new_uri = "$workdir/request.fits";
        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) {
            die("Unable to perform $command error code: $error_code");
        }

        $uri = $new_uri;
    }
}

#  find the name of the output directory
my $product;
if ($ds_id) {
    my $command = "$pstamptool -datastore -ds_id $ds_id";
    $command .= " -dbname $dbname" if $dbname;
    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");
    }

    if (@$stdout_buf == 0) {
        print STDERR "data store with data store id $ds_id not found\n";
        exit 1;
    }
    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
        die("Unable to parse metdata config doc");

    my $dataStores = parse_md_list($metadata);

    my $dataStore = $dataStores->[0];

    $product = $dataStore->{outProduct};
} else {
    $product = $defaultDSProduct;
}

#
# Here is one of the places that we assume that the output for a request goes directly into
# the output fileset directory in the data store.
#
my $outProductDir = "$outputDataStoreRoot/$product";

die "output product directory $outProductDir does not exist" if (! -e $outProductDir) ;
die "file with name of output product directory $outProductDir exists but is not a directory" 
    if (! -d $outProductDir);

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;
    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 $outProductDir --file $uri";
$parse_cmd .= " --dbname $dbname" if $dbname;

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);

    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;
    }
}

#
# 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 run";
    $command   .= " -reqType $reqType" if $reqType;
    $command   .= " -uri $new_uri" if $new_uri;
    $command   .= " -fault $fault" if $fault;
    $command   .= " -dbname $dbname" if $dbname;
    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;
    }
}
