#!/usr/bin/env perl

use warnings;
use strict;
use Carp;

use IPC::Cmd 0.36 qw( can_run run );
#use IPC::Run 0.36 qw( run );
use PS::IPP::Metadata::List qw( parse_md_list );
use PS::IPP::Config 1.01 qw( :standard );
use File::Temp qw( tempfile );
use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
use Pod::Usage qw( pod2usage );

my $REALRUN = 1;

my ( $class_id, $exp_id, $this_uri, $previous_uri, $imfile_state, $camera, $dbname, $logfile, $verbose, $save_temps, $rerun );
GetOptions(
    'exp_id=s'         => \$exp_id,   # exposure identifier
    'class_id=s'       => \$class_id, # chip identifier
    'this_uri=s'       => \$this_uri, # uri for this image
    'previous_uri=s'   => \$previous_uri, # uri for the previous image
    'imfile_state=s'   => \$imfile_state, # current state of the imfile.
    'camera=s'          => \$camera,     # Camera
    'dbname|d=s'        => \$dbname, # Database name
    'logfile=s'         => \$logfile,
    'rerun'             => \$rerun,
    'verbose'           => \$verbose,   # Print to stdout
    'save-temps'        => \$save_temps, # Save temporary files?

    ) or pod2usage( 2 );

pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
pod2usage(
    -msg => "Required options: --class_id --this_uri --dbname",
    -exitval => 3,
    ) unless
    defined $class_id and
    defined $this_uri and
    defined $dbname;

unless (defined $camera) {
    $camera = "GPC1";
}

my $missing_tools;
my $regtool  = can_run('regtool')  or (warn "Can't find regtool" and $missing_tools = 1);
my $funpack  = can_run('funpack')  or (warn "Can't find funpack" and $missing_tools = 1);
my $burntool = can_run('burntool') or (warn "Can't find burntool" and $missing_tools = 1);
my $ppConfigDump = can_run('ppConfigDump') or (warn "Can't find ppConfigDump" and $missing_tools = 1);
my $nebXattr = can_run('neb-xattr') or (warn "Can't find neb-xattr" and $missing_tools = 1);
my $nebreplicate = can_run('neb-replicate') or (warn "Can't find neb-replicate" and $missing_tools = 1);
#my $fpack    = can_run('fpack')    or (warn "Can't find fpack" 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

# IPP configuration (including nebulous)
my $ipprc = PS::IPP::Config->new( $camera ) or my_die("Unable to set up");

unless (defined $logfile) {
    $logfile = $this_uri;
    $logfile =~ s/fits/burn.log/;
}
$ipprc->redirect_output($logfile) if $logfile;

# Determine the value of a "good" burntool run.
my $config_cmd = "$ppConfigDump -camera $camera -dump-camera - | grep BURNTOOL | uniq";
my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
    run ( command => $config_cmd, verbose => $verbose);
unless ($success) {
    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
    &my_die("Unable to perform ppConfigDump: $error_code", $exp_id, $class_id, $PS_EXIT_SYS_ERROR);
}

my $recipeData = $mdcParser->parse(join "", @$stdout_buf) or
    &my_die("Unable to parse metadata config doc", $exp_id, $class_id, $PS_EXIT_SYS_ERROR);

my $burntoolStateGood = 999;
foreach my $cfg (@$recipeData) {
    if ($cfg->{name} eq 'BURNTOOL.STATE.GOOD') {
        $burntoolStateGood = $cfg->{value};
    }
}
if ($burntoolStateGood == 999) {
    &my_die("Failed to determine BURNTOOL.STATE.GOOD", $exp_id, $class_id, $PS_EXIT_SYS_ERROR);
}
my $outState = -1 * abs($burntoolStateGood);

print ">>$burntoolStateGood<<\n";

# Set up files
my $rawImfile = $this_uri;
my $rawImfileReal = $ipprc->file_resolve($rawImfile, 0);

my $outTable  = $this_uri;
$outTable  =~ s/fits$/burn.tbl/;
my $outTableReal = $ipprc->file_resolve($outTable, 1);

my $previousTable;
my $previousTableReal;
if ($previous_uri) {
    $previousTable = $previous_uri;
    $previousTable =~ s/fits$/burn.tbl/;
    $previousTableReal = $ipprc->file_resolve($previousTable, 0);
}

# Set state to processing:
my $burntool_state = 0;

my $status = vsystem ("$regtool -dbname $dbname -updateprocessedimfile -exp_id $exp_id -class_id $class_id -burntool_state -1", $REALRUN);
if ($status) {
    &my_die("failed to update imfile");
}
$burntool_state = 1;


# funpack
my $tempfile = new File::Temp ( TEMPLATE => "burntool.${exp_id}.${class_id}.XXXX",
				DIR => '/tmp/',
				UNLINK => !$save_temps,
				SUFFIX => '.fits');
my $tempPixels = $tempfile->filename;

$status = vsystem ("$funpack -S $rawImfileReal > $tempPixels", $REALRUN);
if ($status) {
    &my_die("failed on funpack",$exp_id,$class_id);
}

# burntool
if (($previousTableReal)&&($previousTableReal ne '')) {
    $status = vsystem ("$burntool $tempPixels in=$previousTableReal out=$outTableReal tableonly=t persist=t", $REALRUN);
}
else {
    $status = vsystem ("$burntool $tempPixels out=$outTableReal tableonly=t persist=t", $REALRUN);
}
if ($status) {
    &my_die("failed on burntool",$exp_id,$class_id);
}
&my_die("Unable to find output file: $outTableReal", $exp_id, $class_id) unless $ipprc->file_exists($outTableReal);

# Replicate files
$status = vsystem ("$nebXattr --write $outTable user.copies:2",$REALRUN);
$status = vsystem ("$nebreplicate --set_copies 2 $outTable",$REALRUN);

# Set state to finished.
my $command = "$regtool -dbname $dbname -updateprocessedimfile -exp_id $exp_id -class_id $class_id -burntool_state $outState";
if ($imfile_state ne 'full') {
    $command .= " -set_state full ";
}
$status = vsystem ($command, $REALRUN);
if ($status) {
    &my_die("failed to update imfile");
}

exit 0;

sub vsystem {
    my $command = shift;
    my $realrun = shift;

    print "$command\n";

    my $status = 0;
    if ($realrun) {
        $status = system ($command);
    }

    return $status;
}

sub my_die {
    my $message = shift;
    if ($#_ != -1) {
        my $exp_id = shift;
        my $class_id = shift;
        vsystem("$regtool -dbname $dbname -updateprocessedimfile -exp_id $exp_id -class_id $class_id -burntool_state 0 -set_state pending_burntool",1);
    }
    printf STDERR "$message\n";
    exit 1;
}


    
