#!/usr/bin/env perl
################################################################################
#
# routine: reserve.pl
#
# purpose: Attempts to create a reservation OSF for each .podlist
#          exposure and the triggering dataset.  These reservation
#          OSFs will be used to avoid collisions in the OTFR science
#          pipeline.  If the triggering dataset is an association,
#          an OSF is created for it in the SCIENCE pipeline, to
#          "prime" association processing there.
#
# environment variables used:
#
# OTFSCI_PATH - name of the path in which an association OSF is created
# ASN_CLASS - used to set the class in the association OSF
# ASN_STEP - used to set the OSF status in the association OSF
# MEM_VAIT - used to set the OSF status in the association OSF
# RESERVATION_PATH - name of the path in which reserve OSFs are created
# OTFSCI_CAL_DIR - calibration directory in the OTFR science pipeline
#
# modification history:
#
#   date    opr     who     reason
# -------- -----  --------  ----------------------------------------------------
# 09/03/00 43165  MSwam     first version
# 09/29/03 49496  MSwam     changes for OTFR asn handling under IDR
# 10/21/03 49496.1 MSwam    replace prints with PrintMsg
# 10/19/04 50663  MSwam     remove unused exps list
# 10/19/04 52106  Sherbert  Add subproducts to the RSVLIST
# 05/19/05 53057  Sherbert  Lowercase product name before create trailer
# 06/03/11 46330  MSwam     Clear last_collected date for each asn member
# 
################################################################################
#
# prepend the Perl include variable using all components of PATH
# (this allows us to reuse other OTFR Perl code)
#
$PATH  = $ENV{"PATH"};
@parts = split /:/, $PATH;
while (defined($val = pop(@parts))) {
    if (substr($val,0,1) eq "/") { unshift(@INC, $val)}
}

#
# include these modules
#
require 'printmsg.pl';       # prints a formatted status message
require 'otfr_setup.pl';     # OTFR pipeline set-up for processing a request
require 'db_get_member_count.pl'; # count of collected members for an asn
require 'db_asn_products.pl';     # get a list of products
require 'db_asn_members_clear_last_collected.pl'; # clear last_collected date

OTFR_setup();
$try_again_later = 2;  # exit status when a reservation can't be made

# from the environment
$science_path = lc($ENV{"OTFSCI_PATH"});
$asn_class    = lc($ENV{"ASN_CLASS"});
$asn_step     = $ENV{"ASN_STEP"};
$mem_vait     = lc($ENV{"MEM_VAIT"});
$RESERVE_PATH = $ENV{"RESERVATION_PATH"};  # the path for pipeline reservations
$caldir       = $ENV{"OTFSCI_CAL_DIR"};
$STDB_REPORT_LEVEL = $ENV{"STDB_REPORT_LEVEL"};

PrintMsg("I","executable is $0");
PrintMsg("I","Processing $p_dir");
@reserve = ();
@products= ();

# get a list of products, if appropriate
if ($p_dset =~ /.*[0-9a-i]$/) {  # product name (ends in 0-9 or a-i)
    @products = DB_asn_products($p_dset);
}

# check for the existence of the reserve list file
$rsvfile = $p_dir . ".rsvlist";
if (! -e $rsvfile) {
    # no file found, create the reserve list and write it to a file
    PrintMsg("D","no .rsvlist file found, creating one...");

   # open the PODLIST file listing exposures and podfile names for input
   $infile = $p_dir . ".podlist";
   if (!open(PODLIST, "$infile")) {
      PrintMsg("E","ERROR: failed to open infile $infile\n"); 
      exit $quit_this_dataset;
   }

   # read each podlist entry, extract the exposures into a list
   #
   @reserve = ();
   while (<PODLIST>) {
      #
      # split the read buffer ($_) into fields and lowercase them
      ($exp, $pod, $indicator) = split(/ /,$_);  
      chomp($indicator);
      $exp = lc($exp);
      #
      # only put entries in the list that are not already there
      $count = grep {/$exp/} @reserve;
      if ($count == 0) {
         push(@reserve, $exp);
      }
   }
   close(PODLIST);

   # add the triggering dataset to the reserve list, if it is not
   # already there (as would be the case for an association)
   #
   $count = grep {/$p_dset/} @reserve;
   if ($count == 0) {
     push(@reserve, $p_dset);
   }

    # add the subproducts to the reserve list, if they are not already
    # there.  I expect the main association product is there, but if I
    # understand this logic correctly, it will not be repeated
    foreach $product ( @products ) {
        $product = lc($product);    ## lowercase for filename compatibility
        $count = grep {/$product/i} @reserve;
        if ($count == 0) {
            push(@reserve, $product);
        }
    }
 
    PrintMsg("D","Products contains @products");
    PrintMsg("D","Reserve  contains @reserve ");

    # write the reserve list to a file
    if (!open(RSVLIST, ">$rsvfile")) {
        PrintMsg("E","ERROR: failed to open outfile $rsvfile\n"); 
        exit $quit_this_dataset;
    }
    foreach $exp (@reserve) {
        $exp = lc($exp);
        PrintMsg("I","need to reserve $exp");
        print RSVLIST "$exp\n";
    }
    close(RSVLIST);
}
else {
    # open the RSVLIST file for input
    if (!open(RSVLIST, "$rsvfile")) {
        PrintMsg("E","ERROR: failed to open infile $rsvfile\n"); 
        exit $quit_this_dataset;
    }

    # reserve list file exists, read it into the reserve list
    @reserve = ();
    while (<RSVLIST>) {
        #
        # save the read buffer ($_), remove the newline, and add to the list
        $exp = $_;  
        chomp($exp);
        PrintMsg("I","need to reserve $exp");
        push(@reserve, $exp);
    }
    close(RSVLIST);
}

# attempt to make reservation OSFs for each exposure and asn
@cancel = ();
foreach $exp (@reserve) {
    $cmd = "osf_create -p $RESERVE_PATH -f $exp -t rsv -s r -n 000";
    PrintMsg("I","$cmd");
    `$cmd`;
    if ($? != 0) {
        PrintMsg("W",
                 "reserve: Failed to create OSF for $exp on $RESERVE_PATH 
                  (collision?)\n ");
        #
        # a collision probably occurred, 
        # delete all reservation OSFs for this request that were just made
        &cancel_reservations;
        PrintMsg("I","will try again later...");
        exit($try_again_later);
    }
    PrintMsg("I","reserve: reservation OSF created for $exp on $RESERVE_PATH");
    push(@cancel, $exp);
}

# all reservations were made OK
# now create a SCIENCE pipeline OSF if the requested dataset is an association
# 
if ($p_dset =~ /.*[0-9a-i]$/) {  # product name (ends in 0-9 or a-i)
    $count = DB_get_member_count($p_dset);
    if ($count == 0) {
        PrintMsg("E","Zero (0) asn records found in asn_members for $p_dset");
        &cancel_reservations;
        exit ($quit_this_dataset);
    }
    $number = $count;
    $numstr = sprintf("%03.3d", $number);
    $cmd = "osf_create -p $science_path -f $p_dset -n $numstr " .
           "-t $asn_class -c \"$asn_step\" -s $mem_vait";
    PrintMsg("I","$cmd");
    `$cmd`;
    if ($? != 0) {
        PrintMsg("E","Unable to create the $science_path osf for $asn");
        &cancel_reservations;
        exit ($quit_this_dataset);
    }

    # clear the asn_members.last_collected date for all ASN members, 
    #   since we will be recollecting that ASN
    #
    DB_asn_members_clear_last_collected($p_dset);

    # create an empty product trailer in the SCIENCE pipeline directory for 
    # a NICMOS association (again, serves to prime the collector)
    # 52106: Do this for each SI's product in the reserve list, not just NIC.
    # 53057: lowercase product name so upper case tra's not left hanging round.
    #
    foreach $product (@products) {
        $product = lc($product);
        open (TRAILER, ">>".$caldir.$product.".tra");
        close(TRAILER);
    }
}

# complete, exit success
PrintMsg("I","reservations complete (all_is_well=$all_is_well)");
exit $all_is_well;

################################################################################
#
# Name: cancel_reservations
#
# Purpose: cancels all reservation OSFs made so far
#
# Globals in: @cancel
#             $RESERVE_PATH 
#
################################################################################
sub cancel_reservations {
    my ($exp); #LOCAL VARS
    #
    # delete all reservation OSFs for this request that were just made
    foreach $exp (@cancel) {
        `osf_delete -p $RESERVE_PATH -f $exp`;
        if ($? == 0) {
            PrintMsg("I","reserve: CANCELLED reservation OSF for $exp on 
                     $RESERVE_PATH\n ");
        }
        else {
            PrintMsg("E","reserve: failed to CANCEL reservation OSF for 
                     $exp on $RESERVE_PATH\n ");
        }
    }
    $?;
}
