#!/bin/env perl

use strict;
use warnings;
use DBI;
use IPC::Cmd 0.36 qw( can_run run );
use PS::IPP::Metadata::Config;
use PS::IPP::Config 1.01 qw( :standard );

use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
use Pod::Usage qw( pod2usage );
use File::Basename;
use IO::File;

my $tooldir = "/home/panstarrs/bills/ipp/tools";
my $dbname = "gpc1";
my ($stage, $stage_id, $component, $threads, $update, $redirect, $pretend, $save_temps, $verbose);
my $zaplog = 1;

GetOptions(
    'stage=s'           => \$stage,
    'stage_id=i'        => \$stage_id,
    'component=s'       => \$component,
    'threads=i'         => \$threads,
    'pretend'           => \$pretend,
    'redirect-output'   => \$redirect,
    'zaplog'            => \$zaplog,
    'update'            => \$update,
    'dbname=s'          => \$dbname,
    'verbose'           => \$verbose, 
    'save-temps'        => \$save_temps,
) or pod2usage( 2 );

pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
pod2usage( -msg => "--stage and --stage_id are required", -exitval => 2 )
    if !$stage or !$stage_id;
pod2usage( -msg => "--component is required for $stage stage", -exitval => 2 ) if !$component and $stage ne 'camera';

my $ipprc =  PS::IPP::Config->new();
my $dbh = getDBHandle();

$component = "" if !defined $component;

my $camQuery = "SELECT fault, concat_ws('.', camProcessedExp.path_base, 'log') AS logfile 
FROM camRun join camProcessedExp using(cam_id) WHERE cam_id = $stage_id";

my $warpQuery = "SELECT fault, skycell_id, concat_ws('.', warpSkyfile.path_base, 'log') AS logfile 
FROM warpRun join warpSkyfile using(warp_id) WHERE warp_id = $stage_id and skycell_id = '$component'";

my $diffQuery = "SELECT fault, skycell_id, concat_ws('.', diffSkyfile.path_base, 'log') AS logfile 
FROM diffRun join diffSkyfile using(diff_id) WHERE diff_id = $stage_id and skycell_id = '$component'";

# list of error strings to look for
my @camList  = ('Failed to convert file name ');
my @warpList = ('Unable to find instance of Nebulous handle ');
my @diffList = ('Unable to find instance of Nebulous handle ');

my $query;
my $list;
if ($stage eq 'camera') {
    $query = $camQuery;
    $list = \@camList;
} elsif ($stage eq 'warp') {
    $query = $warpQuery;
    $list = \@warpList;
} elsif ($stage eq 'diff') {
    $query = $diffQuery;
    $list = \@diffList;
} else {
    die "unexpected stage: $stage";
}

# find the log file
my $stmt = $dbh->prepare($query);
$stmt->execute();
my $results = $stmt->fetchrow_hashref();
die "query returned no results\n" if !$results;

my $logfile = $results->{logfile};
die "log file name not found\n" if !$logfile;

my $fault = $results->{fault};
die "stage $stage_id $component not faulted\n" unless $fault;

my $skycell_id = $results->{skycell_id};

my $resolved = `neb-locate -p $logfile`;
chomp $resolved;
die "failed to locate log file for $stage $stage_id $component" if !$resolved;
die "failed to access log file for $stage $stage_id $component" if ! -r $resolved;

my $log = `tail -n 100 $resolved`;

my @lines = split '\n', $log;
my $match_line;
my $badfile;
foreach my $line (@lines) {
    foreach my $str (@$list) {
        if ($line =~ /$str/) {
            print STDERR "found $str in $line\n" if $verbose;
            $match_line = $line;

            # fine the str in the line
            my $n = index($line, $str);
            # create a substring containing the text after $str
            my $substr = substr $line, $n + length($str);
            # The first word is the nebulous pathname for the bad file
            ($badfile) = split " ", $substr;
            $badfile =~ s/://;
            last;
        }
    }
    last if $match_line;
}

if (!$match_line or !$badfile) {
    die "unable to resolve fault for $stage $stage_id $component\n";
}
print "Bad file is: $badfile\n" if $verbose;

my $base = basename($badfile);
print "base: $base\n" if $verbose;

my $command;
if ($stage eq 'camera') {
    my ($exp_name, $exp_id, undef, $chip_id, $class_id) = split '\.', $base;
    $command = "runchipimfile.pl --chip_id $chip_id --class_id $class_id --redirect-output";
} elsif ($stage eq 'warp') {
    if ($base =~ /\.ch\./) {
        # problem is with a chip output
        my ($exp_name, $exp_id, undef, $chip_id, $class_id) = split '\.', $base;
        $command = "runchipimfile.pl --chip_id $chip_id --class_id $class_id --redirect-output";
    } else {
        die "don't know how to deal with bad $badfile yet\n";
    }
} elsif ($stage eq 'diff') {
    if ($base =~ /\.wrp\./) {
        # problem is with a warp output
        my ($exp_name, $exp_id, undef, $warp_id) = split '\.', $base;
        $command = "runwarpskycell.pl --warp_id $warp_id --skycell_id $skycell_id --redirect-output";
    } else {
        die "don't know how to deal with bad $badfile yet\n";
    }
} else {
    die "not done with $stage yet\n";
}


$command .= " --zaplog" if $zaplog;

if (!$pretend) {
    my $rc = system "perl $tooldir/$command";
    if ($rc) {
        my $status = $rc >> 8;
        die "$command failed with $rc $status";
    }
} else {
    print "skipping: $command\n";
}

exit 0;


sub getDBHandle {
    my $dbserver = metadataLookupStr($ipprc->{_siteConfig}, "DBSERVER");
    my $dbuser = metadataLookupStr($ipprc->{_siteConfig}, "DBUSER");
    my $dbpassword = metadataLookupStr($ipprc->{_siteConfig}, "DBPASSWORD");
    if (!$dbname) {
        $dbname = metadataLookupStr($ipprc->{_siteConfig}, "DBNAME");
    }

    die "database configuration set up" unless defined($dbserver) and defined($dbuser)
        and defined($dbpassword) and defined($dbname);

    my $dsn = "DBI:mysql:host=$dbserver;database=$dbname";

    my $dbh = DBI->connect($dsn, $dbuser, $dbpassword) 
        or die "Cannot connect to database.\n";

    return $dbh;
}

__END__

=pod

=head1 NAME

regenerateinput.pl - regenerate missing or damaged inputs for a component

=head1 SYNOPSIS
    
    XXX: pod TODO

    perl runwarpskyfile.pl --warp_id <warp_id> --skycell_id <skycell_id> [--threads <num_threads>] [--update] [--redirect-output] [--pretend] [--dbname <dbname>]

Query the database for the results of a warp skycell and rerun the processing, optionally reverting a faulted
run.

=over 4

=item * --warp_id <warp_id>

The id of the warpSkyfile run to process.

=item * --skycell_id <skycell_id>

The skycell_id of the warpSkyfile run to process.

=item * --threads <num_threads>

The number of threads to use. Default 1.
Optional.


=item * --update

Revert a faulted skycell before processing. If the skycell is not faulted no processing is done.
WARNING: insure that the standard science procesing either has warp stage turned off or the
label of this warpRun ommited from the list of labels otherwise it may attempt to processes this run at the same time.
Optional.

=item *  --redirect-output

Send the output of the command to the logfile. (This is the default if --update is supplied).
Optional.

=item * --pretend

Just print the commands that would be executed, but do not run them.
Optional.

=item * --dbname <dbname>

Name of the IPP databse to query. Default is 'gpc1'.
Optional.


=head1 SEE ALSO
L<runchipimfile.pl>, L<runcameraexp.pl>, L<rundiffskyfile.pl>

=cut
