#!/bin/env perl
###
### pstamp_job_run.pl
###
###     Run a given postage stamp Job
###

use warnings;
use strict;

use Sys::Hostname;
use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
use File::Basename;
use Digest::MD5::File qw( file_md5_hex );
use PStamp::RequestFile qw( :standard );

my $verbose;
my $dbname;
my $job_id;
my $redirect_output;
my $output_base;

my $host = hostname();


GetOptions(
    'job_id=s'      =>  \$job_id,
    'output_base=s' =>  \$output_base,
    'redirect-output' => \$redirect_output,
    'dbname=s'      =>  \$dbname,
    'verbose'       =>  \$verbose,
);


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

die "job_id is required" if !$job_id;
die "output_base is required" if !$output_base;

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

if ($redirect_output) {
    my $logDest = "$output_base.log";
    $ipprc->redirect_output($logDest);
}

my $missing_tools;

my $pstamptool = can_run('pstamptool')  or (warn "Can't find pstamptool"  and $missing_tools = 1);
my $ppstamp    = can_run('ppstamp') or (warn "Can't find ppstamp" and $missing_tools = 1);
my $pstamp_get_image_job    = can_run('pstamp_get_image_job.pl') or (warn "Can't find pstamp_get_image_job.pl" 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 $psjob;
#Look up the uri for the given job
{
    my $command = "$pstamptool -pendingjob -job_id $job_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 pstamptool -pendingjob: $error_code");
    }

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

    my $jobs = parse_md_list($metadata);
    my $num = @$jobs;
    die "unexpected number of jobs $num found for job: $job_id" if $num > 1;

    $psjob = $jobs->[0];
}

if (!$psjob) {
    print STDERR "postage stamp job $job_id not found\n";
    exit 1;
}

my $rownum = $psjob->{rownum};
my $uri = $psjob->{uri};
my $outputBase = $psjob->{outputBase};
my $argString = $psjob->{args};
my $jobType = $psjob->{jobType};

my $jobStatus;
if ($jobType eq "stamp") {
    my $command = "$ppstamp -file $uri $outputBase $argString";
    $command .= " -dbname $dbname" if $dbname;
    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
        run(command => $command, verbose => $verbose);

    if ($success) {
        # XXX shouldn't need to do this, review schema`
        my $dir = dirname($outputBase);

        my $reglist = "$dir/reglist$job_id";

        open F, ">$reglist" or die "can't open $reglist for output";

        # figure out what output images we should expect

        # Note: we are assuming the contents of the filerules here.
        my @extensions = ( "fits", "mk.fits", "wt.fits");

        # we always require an image
        my $output_mask = $PSTAMP_SELECT_IMAGE;

        # we search the argString for -mask and -weight. 
        # searching the arg string allows us to avoid adding a column in pstampJob
        # XXX yeah but does this make us susceptible to user input fouling us up?
        # For example, what if somebody sets the output base to file-mask-weight
        # TODO: clean this up
        $output_mask |= $PSTAMP_SELECT_MASK   if ($argString =~ /-mask/);
        $output_mask |= $PSTAMP_SELECT_WEIGHT if ($argString =~ /-weight/);

        # XXX we're getting a bit intimate with the bit field definitions here.
        # do better use a hash

        my $m = 1;
        foreach my $extension (@extensions) {
            my $do_this_one = $m & $output_mask;
            $m = $m << 1;
            next if (! $do_this_one);

            my $basename = basename($outputBase);

            my $filename = "${basename}.${extension}";
            my $path  = "${outputBase}.${extension}";

            # XXX is pstamp always the right file type, if not where do we get the right one?
            print F file_registration_line($filename, $path, "pstamp") . "\n";
        }

        close F;
        $jobStatus = $PS_EXIT_SUCCESS;
    } else {
        $jobStatus = $error_code >> 8;
        print STDERR "ppstamp failed with error code: $jobStatus\n";
    }
} elsif ($jobType eq "get_image") {
    my $command = "$pstamp_get_image_job --job_id $job_id --uri $uri --out_dir $outputBase --rownum $rownum";
    $command .= " --dbname $dbname" if $dbname;
    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
        run(command => $command, verbose => $verbose);

    if ($success) {
        $jobStatus = $PS_EXIT_SUCCESS;
    } else {
        $jobStatus = $error_code >> 8;
        print STDERR "ppstamp failed with error code: $jobStatus\n";
    }
} elsif ($jobType eq "detect_query") {
    die("multiple detect_query jobs not supported yet");
} else {
    die("unknown jobType $jobType found");
}

# stop the job and set the result value
{
    my $command = "$pstamptool -updatejob -job_id $job_id -state stop -fault $jobStatus";
    $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");
    }
}

exit $jobStatus;

# create a string to be passed as input to dsreg when registering this file in a fileset
# XXX move this to a module so it can be shared

sub file_registration_line {
    my $filename = shift;
    my $path     = shift;
    my $filetype = shift;
    if (-e $path) {
        my @finfo = stat($path);
        die "failed to stat $path" unless (@finfo);    # XXX clean up
        my $bytes = $finfo[7];
        my $md5sum = file_md5_hex($path);

        return "$filename|$bytes|$md5sum|$filetype|";
    } else {
        die "$filename not found at $path";
    }
}
