#!/usr/bin/env perl

use warnings;
use strict;

use LWP::UserAgent;
use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
use XML::LibXML;
use File::Temp qw(tempfile);
use ippToPsps::IppToPspsDb;
use ippToPsps::Datastore;
use ippToPsps::Batch;

my $singleBatch = undef;
my $verbose = undef;
my $save_temps = undef;
my $fromTime = undef;
my $toTime = undef;
my $product = undef;
my $filePath = undef;
my $removeUnprocessed = undef;
my $onlyCheckUndeleted = undef;
my $onlyCheckMergeWorthy = undef;
my $onlyCheckMerged = undef;


GetOptions(
        'batch|b=s' => \$singleBatch,
        'from|f=s' => \$fromTime,
        'to|t=s' => \$toTime,
        'product|p=s' => \$product,
        'location|l=s' => \$filePath,
        'onlyundeleted|u' => \$onlyCheckUndeleted,
        'onlymergeworthy|w' => \$onlyCheckMergeWorthy,
        'onlymerged|m' => \$onlyCheckMerged,
        'remove|r' => \$removeUnprocessed,
        'verbose|v' => \$verbose,
        'save_temps|s' => \$save_temps
        );

if (@ARGV) {
    print "* UNKNKOWN: option                          @ARGV\n";
}
if (!defined $product) {
    print "* OPTIONAL: a datastore product name            -p <name>\n";
}
if (!defined $singleBatch) {
    print "* OPTIONAL: a single batch                      -b <batchNum>        (default = none)\n";
}
if (!defined $fromTime) {
    $fromTime = "2010-01-01";
    print "* OPTIONAL: from time                           -f <dateTime>        (default = $fromTime)\n";
}
if (!defined $toTime) {
    $toTime = "2099-12-31";
    print "* OPTIONAL: to time                             -t <dateTime>        (default = $toTime)\n";
}
if (!defined $filePath) {
    print "* OPTIONAL: location for files to be deleted    -l <path>\n";
}
if (!defined $onlyCheckMerged) {
    $onlyCheckMerged = 0;
    print "* OPTIONAL: only check merged batches           -m                   (default = $onlyCheckMerged)\n";
}
if (!defined $onlyCheckMergeWorthy) {
    $onlyCheckMergeWorthy = 0;
    print "* OPTIONAL: only check merge worthy batches     -w                   (default = $onlyCheckMergeWorthy)\n";
}
if (!defined $onlyCheckUndeleted) {
    $onlyCheckUndeleted = 0;
    print "* OPTIONAL: only check undeleted batches        -u                   (default = $onlyCheckUndeleted)\n";
}
if (!defined $removeUnprocessed) {
    $removeUnprocessed = 0;
    print "* OPTIONAL: remove unprocessed files            -r                   (default = $removeUnprocessed)\n";
}
if (!defined $verbose) {
    $verbose = 0;
    print "* OPTIONAL: run in verbose mode                 -v                   (default = $verbose)\n";
}
if (!defined $save_temps) {
    $save_temps = 0;
    print "* OPTIONAL: keep temp files                     -t                   (default = $save_temps)\n";
}


my $datastore = new ippToPsps::Datastore($product, 0, 0);
my $ippToPspsDb = new ippToPsps::IppToPspsDb("ippToPsps", "ippdb01", "ipp", "ipp", $verbose, $save_temps);
my $odmUrl = "http://web01.psps.ifa.hawaii.edu/a01/OdmWebService/OdmWebService.asmx/GetBatchStatus";
my $ua = LWP::UserAgent->new;
$ua->timeout(15);
$ua->env_proxy;


process();

#######################################################################################
# 
# Loops through all processed exposures and checks against the ODM, then deletes if necessary 
# 
########################################################################################
sub process {

    my $batches;
    my $numOfBatches;

    if (defined $singleBatch ) { $numOfBatches = $ippToPspsDb->getBatch($singleBatch, \$batches);}
    else { $numOfBatches = $ippToPspsDb->getBatches(\$batches, $fromTime, $toTime);}

    if ($numOfBatches < 1) {return 0;}

    print "\n";
    printf("+----------------------+--------------+--------------+----------------+--------------+--------------+---------+----------+\n");
    printf("|      Timestamp       |    Batch     |  Exposure ID | Loaded to ODM? | Load failed? |Merge worthy? | Merged? | Deleted? |\n");
    printf("+----------------------+--------------+--------------+----------------+--------------+--------------+---------+----------+\n");

    # loop round batches
    my $batch;
    my $numBatchesToCheck = 0;
    my $numChecked = 0;
    my $numRemovedFromDatastore = 0;
    my $numDeleted = 0;
    my $newMerged = 0;
    foreach $batch ( @{$batches} ) {
        my ($timestamp, 
                $type, 
                $expId, 
                $batchId, 
                $surveyType,
                $deleted,
                $dvoDb, 
                $processed, 
                $onDatastore, 
                $loadedToOdm, 
                $loadFailed, 
                $mergeWorthy, 
                $merged) = @{$batch};

        if (!$onDatastore) {next;}
        if ($onlyCheckUndeleted && $deleted) {next;}
        if ($onlyCheckMergeWorthy && (!$mergeWorthy || $merged)) {next;}
        if ($onlyCheckMerged && !$merged) {next;}

        my $batch = ippToPsps::Batch->existing(
                undef,
                undef,
                $batchId, 
                $ippToPspsDb, 
                $filePath, 
                $verbose, 
                $save_temps);

        if (!$processed) {

            if ($removeUnprocessed && defined $filePath) {

                if ($batch->deleteDir()) {$numDeleted++;}
            }
            next;
        }

        $numBatchesToCheck++;

        # if not merged or failed load, then update by polling ODM for status
        if (!$merged && !$loadFailed) {
            if (checkODM($batch->getName(), \$loadedToOdm, \$loadFailed, \$mergeWorthy, \$merged)) {
                
                $numChecked++;
                if ($merged) {$newMerged++;}
            }
            else {next;}
        }

        # delete from datastore
        if (defined $product && !$deleted && $loadedToOdm && ($mergeWorthy || $loadFailed)) {


            $deleted = $datastore->remove($batch->getName());
            if ($deleted) {
                $ippToPspsDb->setBatchAsDeleted($batchId, $expId);
                $numRemovedFromDatastore++;
            }
        }
        # if merged and already removed from datastore then delete files
        if (defined $filePath && $mergeWorthy && $deleted) {

            if($batch->deleteFile($batchId)) {$numDeleted++;}
        }

        # update database
        $ippToPspsDb->updateODMStatus($batchId, $expId, $loadedToOdm, $loadFailed, $mergeWorthy, $merged, $deleted);
        printf( "| %18s  | %11s  | %10d   |    %6s      |    %6s    |   %6s     | %6s  |  %5s   |\n", 
                $timestamp, 
                $batch->getName(), 
                $expId, 
                $loadedToOdm ? "yes" : "no", 
                $loadFailed ? "yes" : "no", 
                $mergeWorthy ? "yes" : "no", 
                $merged ? "yes" : "no",
                $deleted ? "yes" : "no");
    }

    printf("+----------------------+--------------+--------------+----------------+--------------+--------------+---------+----------+\n");
    printf( "* Successfully checked %d batch%s out of %d\n", $numChecked, ($numChecked==1) ? "" : "es", $numBatchesToCheck);
    printf( "* %d newly merged batches\n", $newMerged);
    printf( "* Successfully removed %d batch%s from the datastore\n", $numRemovedFromDatastore, ($numRemovedFromDatastore==1) ? "" : "es");
    printf( "* Successfully deleted %d batch%s from local file system\n", $numDeleted, ($numDeleted==1) ? "" : "es");

}

########################################################################################
# 
# Check a single batch
# 
########################################################################################
sub checkODM {
    my ($batchName, $loadedToOdm, $loadFailed, $mergeWorthy, $merged) = @_;

    my $statusFilter = "*";

    my $response = $ua->post($odmUrl,
            [batchNameFilter => "%".$batchName,
            statusFilter => $statusFilter,
            fromFilter => "2010-01-01",
            toFilter => "2099-01-01"]
            );

    # '200' is the 'OK' response
    if ($response->code != 200) {

        print "Problem connecting to web service for '$batchName', HTTP response status: ".$response->status_line."\n";
        return 0;
    }
    #        print( "HTTP response status: ".$response->content."\n" );

    my ($tempFile, $tempName) = tempfile( "/tmp/ippToPsps_odmXml.XXXX", UNLINK => !$save_temps);
    if ($verbose) {print $response->content. "\n";}
    print $tempFile $response->content;
    close($tempFile);

    ${$loadedToOdm} = 0;
    ${$loadFailed} = 0;
    ${$mergeWorthy} = 0;
    ${$merged} = 0;

    parseXml($tempName, $loadedToOdm, $loadFailed, $mergeWorthy, $merged);

    unlink($tempFile);

    return 1;
}

########################################################################################
# 
# Parses ODM XML
# 
########################################################################################
sub parseXml {
    my ($xmlFile, $loadedToOdm, $loadFailed, $mergeWorthy, $merged) = @_;

    my $parser = XML::LibXML->new;
    my $doc = $parser->parse_file($xmlFile);
    my $xc = XML::LibXML::XPathContext->new($doc);
    $xc->registerNs('ArrayOfOdmBatchState', 'PanSTARRS.Services.OdmWebService');
    my $result = $xc->findvalue('//ArrayOfOdmBatchState:BatchState');

    if ($result =~ m/failed/) { 
        ${$loadFailed} = 1;
        ${$loadedToOdm} = 1;
    return;
    }

    $result = $xc->findvalue('//ArrayOfOdmBatchState:Message');
    ${$loadedToOdm} = 0;
    ${$mergeWorthy} = 0;
    ${$merged} = 0;

    if ($result =~ m/None/) { ${$loadedToOdm} = 1;}
    if ($result =~ m/LoadStarted/) { ${$loadedToOdm} = 1;}
    if ($result =~ m/MergeWorthy/) { ${$loadedToOdm} = 1; ${$mergeWorthy} = 1;}
    if ($result =~ m/Merge[1-9]Completed/) { ${$loadedToOdm} = 1; ${$mergeWorthy} = 1; ${$merged} = 1;}
}

