#!/usr/bin/env perl
#
# parse a MOPS_DETCTABILITY_QUERY table and create a results file
#

use strict;
use warnings;
use Carp;
use Getopt::Long qw( GetOptions );
use Pod::Usage qw( pod2usage );
use IPC::Cmd 0.36 qw( can_run run );

use PS::IPP::PStamp::RequestFile qw( :standard );
use PS::IPP::PStamp::Job qw( :standard );
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 ($no_update, $imagedb, $label);
my ($req_file, $req_id, $outdir, $product, $mode, $dbname, $dbserver, $verbose, $save_temps);
my ($job_id,$rownum); # stuff from the post-update world
#
# parse args
#

GetOptions(
        'file=s'          =>      \$req_file,
        'req_id=s'        =>      \$req_id,
        'job_id=s'        =>      \$job_id,
        'rownum=s'        =>      \$rownum,
        'outdir=s'       =>      \$outdir,
        'label=s'         =>      \$label,
        'product=s'       =>      \$product,
        'mode=s'          =>      \$mode,
        'dbname=s'        =>      \$dbname,
        'dbserver=s'      =>      \$dbserver,
        'verbose'         =>      \$verbose,
        'save-temps'      =>      \$save_temps,
) or pod2usage(2);

die "invalid mode '$mode'" unless ($mode eq "list_uri") or ($mode eq "queue_job");
die "--file or --job_id is required" if !$req_file;

if ($mode ne "list_uri") {
    die "req_id is required" if !$req_id;
    die "outdir is required" if !$outdir;
    die "product is required" if !$product;
}
my $missing_tools;
my $pstamptool = can_run('pstamptool') or (warn "Can't find pstamptool" and $missing_tools =1);
my $detectresponse = can_run('detectability_respond.pl') or 
    (warn "Can't find detectability_respond.pl" and $missing_tools = 1);
my $fields = can_run('fields') or (warn "Can't find fields" and $missing_tools =1);

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

if (!$dbserver) {
    my $ipprc = PS::IPP::Config->new();
    $dbserver =  metadataLookupStr($ipprc->{_siteConfig}, 'PS_DBSERVER');
}

# just deal with these arguments once and for all
$pstamptool .= " -dbname $dbname" if $dbname;
$pstamptool .= " -dbserver $dbserver" if $dbserver;
$detectresponse .= " --dbname $dbname" if $dbname;

$no_update = 1 if $mode eq "list_job";

# Unless we're running as a job, write the parse arguments in case we need to rerun this parsing.
if (!$job_id) {
    my $argslist = "$outdir/parse.args";
    open ARGSLIST, ">$argslist" or my_die("failed to open argslist file $argslist", $job_id, $PS_EXIT_UNKNOWN_ERROR);
    print ARGSLIST "--label $label --mode $mode --req_id $req_id --product $product --outdir $outdir --file $req_file\n";
    close ARGSLIST;
}

# get the query id and check the extname and version from the header
my $fields_output; 
{
    my $command = "echo $req_file | $fields -x 0 EXTNAME EXTVER QUERY_ID";
    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
        run(command => $command, verbose => $verbose);
    
    $fields_output = join "", @$stdout_buf;
}
my (undef, $extname, $extver, $req_name) = split " ", $fields_output;

my_die("$req_file is missing one of EXTNAME EXTVER or QUERY_ID", $PS_EXIT_PROG_ERROR)
    if !(defined($extname) and defined($extver) and defined($req_name));
my_die("$req_file has EXTNAME $extname not MOPS_DETECTABILITY_QUERY table", $PS_EXIT_PROG_ERROR)
    if $extname ne "MOPS_DETECTABILITY_QUERY";
my_die("$req_file is version $extver expecting 1", $PS_EXIT_PROG_ERROR)
    if $extver ne 1;

# Set up the workdir for this query.
if (! -e $outdir ) {
    mkdir $outdir or my_die("cannot create output directory $outdir", $PS_EXIT_PROG_ERROR);
} elsif (! -d $outdir ) {
    my_die ("output fileset directory $outdir exists but is not a directory", $PS_EXIT_PROG_ERROR);
}


# Pass along the request file to the response generator.
my $response_file = "$outdir/${req_name}.dresponse.${req_id}.fits";
my $fault;
my $data_to_update = '';
{
    my $command = "$detectresponse --input $req_file --output $response_file --workdir $outdir";
    $command .= " --save-temps" if $save_temps;
    $command .= " --verbose" if $verbose;

    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
        run(command => $command, verbose => $verbose);
    unless ($success) {
        warn("Warn! Unable to perform $command error code: $error_code");
    }
    # Use the fault code to see if we can regenerate the missing data.
    $fault = $error_code >> 8;
    if ($fault == $PSTAMP_NOT_AVAILABLE) {
	$data_to_update = (split /\n/, (join "", @$stdout_buf))[-1];	
    }	
}

my $result;
# If we returned correctly with a valid response file, get a job ID
# for the completed work, and move the response to a standardized name.
if ($fault == 0) {
    my $command = "$pstamptool -addjob -req_id $req_id -outputBase $outdir"; 
    $command .= " -job_type detect_query -state stop -fault 0";
    $command .= " -rownum 1";

    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
        run(command => $command, verbose => $verbose);
    if ($success) {
        $job_id = join "", @$stdout_buf;
        chomp $job_id;
        if ($job_id && -e $response_file) {
            rename $response_file, "$outdir/response${job_id}.fits";
        }
        $result = 0;
    } else {
        warn("Unable to perform $command error code: $error_code");
        $result = $error_code >> 8;
    }
}
else {
    # Failed to run correctly, which means that we need to queue a job and flag data for updating.
    # Get the dependency id for the data we're requesting be updated.
    my $dep_id = queue_update_run($req_id,$job_id,$outdir,$label,$data_to_update);

    # Link this request to a job and link that job to any dependency
    my $command = "$pstamptool -addjob -req_id $req_id -outputBase $outdir"; 
    $command .= " -job_type detect_query -state run -fault 0";
    $command .= " -rownum 1";
    $command .= " -dep_id $dep_id" if $dep_id;

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

    if ($success) {
        $job_id = join "", @$stdout_buf;
        chomp $job_id;
        if ($job_id && -e $response_file) {
	    # We shouldn't have a response file at this stage.
            rename $response_file, "$outdir/response${job_id}.fits";
        }
        $result = 0;
    } else {
        warn("Unable to perform $command error code: $error_code");
        $result = $error_code >> 8;
    }
}

# This does not set the request state to stop.  That will happen with the request_finish.pl script, 
# which will notice that we've inserted the stopped job and decide we're finished. Easy enough.
{
    my $command = "$pstamptool -updatereq -req_id $req_id -set_name $req_name -set_outProduct $product";
    $command .= " -set_fault $result" if $result;

    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
        run(command => $command, verbose => $verbose);
    unless ($success) {
        my_die("$command failed",$PS_EXIT_UNKNOWN_ERROR);
    }
}
 
exit 0;


# If we have to queue an update run, do so and create a new dependent
sub queue_update_run {
    my ($req_id, $job_id, $outdir, $label, $data_to_update) = @_;

    my ($state, $stage, $stage_id, $component, $need_magic, $imagedb) = split /\s+/, $data_to_update;
    
    if (($state ne 'cleaned') and ($state ne 'update') and ($state ne 'goto_cleaned')) {
	# We should have received one of these states, so if not, signal that we have a problem.
	my_die("$stage $stage_id is in unexpected state $state", $PS_EXIT_PROG_ERROR);
    }
    my $dep_id;
    my $command = "$pstamptool -getdependent -stage $stage -stage_id $stage_id -imagedb $imagedb -component $component ";
    $command .= " -outdir $outdir";
    $command .= " -need_magic" if $need_magic;

    my $rlabel = "dq_ud_" . $label if $label;
    $command .= " -rlabel $rlabel" if $rlabel;

    if (!$no_update) {
	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
	    run(command => $command, verbose => $verbose);
	unless ($success) {
	    my_die("$command failed", $PS_EXIT_UNKNOWN_ERROR);
	}
	my $output = join "", @$stdout_buf;
	chomp $output;
	$dep_id = $output;

	my_die("pstamptool -getdependent returned invalid dep_id", $PS_EXIT_PROG_ERROR) if !$dep_id;
    } 
    else {
	print STDERR "skipping $command\n";
	$dep_id = 42;
    }

    return($dep_id);
}
	
    

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

    carp $msg;

    # we don't fault the request here pstamp_parser_run.pl handles that if necessary
    exit $fault;
}
