#!/usr/bin/env perl

=head1 NAME

=head1 SYNOPSIS

  CreateSubProjectPrimers.pl [options]

  Options:
  -if <file>    input file of sub-project paths
  -log <file>   log file (optional; default is createSubPRojectPrimers.pl.log)
  -warn <file>  warnings log file (optional; default is defined in gapRes.config)
  -h            detailed message (optional)

=head1 DESCRIPTION

This is a wrapper program for the Gap Resolution software.
It takes a list of Gap project directories and then attempts
to design primers for a gap and places the designed primers 
in the gap directory and also in a file in the current working 
directory.

For eachs of the sub project directories the program performs the 
following:

1) Check for the existence of a fasta file and associated primer info 
   file. 

2) If the appropriate files exist then primer3 is called on the files

3) If primers were successfully designed, an output file is created within
   the gap project sub directory.  An entry is also added to the primer 
   file in the current working directory.

The output files created are based on the parameters below are:

      {gapname}createSubProjectPrimers.subProjectPrimersExtension
      {current workingdir}/createSubProjectPrimers.allProjectPrimersFile

The output file format is as follows:

  name=<primer_name>
  position=<contig> <primerStart> <primerEnd>
  type=<primer type>
  sequence=<sequence>
  template=<clone1> <clone2> ...
  date=YYMMDD:HHMMSS temp=<annealing temp>

Parameters are read from the configuration file and are as follows:

createSubProjectPrimers.primer3ParameterFile=
    primer3.parameters file
createSubProjectPrimers.primerInfoFile=
    name of  primer info file ex. "primerinfo.txt"
createSubProjectPrimers.gapFastaFileExtension=
    fasta file extension ex. ".gap.fasta"
createSubProjectPrimers.subProjectPrimersExtension=
    file extension ex. ".subProjectPrimers.txt"
createSubProjectPrimers.allProjectPrimersFile=
    global primer files in working dir ex. "primers.txt"

This wrapper executes the following scripts/programs:

primer3Design.pl

=head1 VERSION

$Revision: 1.11 $

$Date: 2010-01-06 22:00:39 $

=head1 AUTHOR(S)

Brian Foster

=head1 HISTORY

=over

=item *

Brian Foster 06Jan08 creation

=item *

S.Trong 08/05/2009 - added ability to skip and create warnings file if sub project fails.

=item *

S.Trong 12/29/2009 - added -log and -warn options.

=back

=cut

use strict;
use warnings;
use Pod::Usage;
use Cwd;
use Cwd qw(abs_path);
use Carp;
use Carp qw(cluck);
use Getopt::Long;
use File::Path;
use File::Copy;
use File::Basename;
use FindBin qw($RealBin);
use lib "$RealBin/../lib";
use PGF::Utilities::Properties;
use PGF::Utilities::RunProcess qw(runProcess);
use PGF::Utilities::Logger;
use PGF::GapResolution::Warnings;
use PGF::GapResolution::ParseGapValidationInfo;
use vars qw( $optHelp $optGapDir $optLogFile $optWarningsFile );

#============================================================================#
# INPUT VALIDATION
#============================================================================#
my $programExecution = abs_path(dirname($0))."/".basename($0)." @ARGV";

if( !GetOptions(
	"if=s"=>\$optGapDir,
        "log=s"=>\$optLogFile,
        "warn=s"=>\$optWarningsFile,
        "h"=>\$optHelp,
    )
) {
    printhelp(1);
}

printhelp(2) if $optHelp;

unless($optGapDir){
    print STDERR "please specify -if option\n";
    printhelp(1);
}

#============================================================================#
# INITIALIZE VARIABLES
#============================================================================#

my $DEBUG = 0;
my $configFile = defined $ENV{GAPRES_CONFIG} ?
    $ENV{GAPRES_CONFIG} : "$RealBin/../config/gapRes.config";
my $outputDir = getcwd;
my $OBJ_PROPS = PGF::Utilities::Properties->new(-configFile=>$configFile);
   $OBJ_PROPS->setExceptionIfEntryNotFound(1); # confess if entry in config file
                                               # is not found.
my $OBJ_LOGGER = PGF::Utilities::Logger->new();
my $OBJ_WARNINGS = PGF::GapResolution::Warnings->new(
    path=>$outputDir, logger=>$OBJ_LOGGER);
   $OBJ_WARNINGS->setLogFile($optWarningsFile) if $optWarningsFile;

#============================================================================#
# INITIALIZATION
#============================================================================#

my $logfile = $optLogFile ? $optLogFile : "$outputDir/".basename($0).".log";

# Set path for logging.
#
setFileForLogging($logfile);

# Log execution into log file.
#
logExecution($programExecution);

#============================================================================#
# VALIDATION
#============================================================================#
my $errMsg;
unless (checkForFileExistence($optGapDir)){
    $errMsg = "-if $optGapDir does not exist or is zero size\n";
    print $errMsg;
    logError($errMsg,0);
    printhelp(1);
}

my $primer3Params = $OBJ_PROPS->getProperty("createSubProjectPrimers.primer3ParameterFile");

if ($primer3Params =~ /\//){
    # full path;
    if (! checkForFileExistence($primer3Params)){
	$errMsg = "Full path $primer3Params does not exist or is zero size\n";
	print $errMsg;
	logError($errMsg,1);
    }
}
else{
    #default path
    $primer3Params = "$RealBin/../config/" . $primer3Params;
    if (! checkForFileExistence($primer3Params)){
	$errMsg ="Default path $primer3Params does not exist or is zero size\n";
	print $errMsg;
	logError($errMsg,1);
    }
}

my $allPrimersOut = "$outputDir/".
    $OBJ_PROPS->getProperty("createSubProjectPrimers.allProjectPrimersFile");


if (checkForFileExistence($allPrimersOut,0)){
    logOutput("WARNING for primer3 output $allPrimersOut exists ...\n",1);
}

$ENV{PRIMER3_PATH} = $OBJ_PROPS->getProperty("script.primer3_core");

if (! checkForFileExistence($ENV{PRIMER3_PATH},0)){
    logError("primer3_core $ENV{PRIMER3_PATH} does not exist",1);
}

if (! checkForFileExistence($primer3Params,0)){
    logError("primer3.parameters $primer3Params does not exist",1);
}



#============================================================================#
# MAIN
#============================================================================#

my $validInfoFileName = $OBJ_PROPS->getProperty(
    "validateSubProject.validateGapAssemblyOutputFile");

foreach my $subProjectDir (&getSubProjectDirectories($optGapDir)){
    
    # Add sub project dir name to warnings.
    #
    $OBJ_WARNINGS->setSubProjectName($subProjectDir);
    $subProjectDir =~ s/\/$//; # remove any trailing '/'
    
    my $validInfoFile = "$subProjectDir/$validInfoFileName";
    
    next if !$OBJ_WARNINGS->fileExists($validInfoFile);
    
    my $objValidInfo = PGF::GapResolution::ParseGapValidationInfo->new($validInfoFile);
    
    # If doPrimerDesign=0 in validinfo.txt file, then skip.
    #
    next if !$objValidInfo->doPrimerDesign;
    
    logOutput("Checking for primer3 inputs $subProjectDir ...",0);
       
    my $subProjectName = basename($subProjectDir);
    my $gapFasta = $subProjectDir . '/' . $subProjectName . 
        $OBJ_PROPS->getProperty("createSubProjectPrimers.gapFastaFileExtension");
    my $primerInfo = $subProjectDir . '/' . 
        $OBJ_PROPS->getProperty("createSubProjectPrimers.primerInfoFile");
    my $primerOutput = $subProjectDir . '/' . $subProjectName . 
        $OBJ_PROPS->getProperty("createSubProjectPrimers.subProjectPrimersExtension");

    next if !-e $gapFasta;
    next if !$OBJ_WARNINGS->fileExists($primerInfo);

    logOutput("running primer3 for $subProjectDir ...",0);

    &run_primer3Design($gapFasta,$primerInfo,$primerOutput,$primer3Params); 

    logOutput("checking for primer3 outputs for $subProjectDir ...",0);

    if ( $OBJ_WARNINGS->fileExists($primerOutput) ){
	logOutput("$primerOutput file created\n",1);
	open(ALL, ">> $allPrimersOut") or confess "can't open $allPrimersOut for appending\n";
	open(SUB, "$primerOutput") or confess "can't open $primerOutput for reading\n";
	while (my $line = <SUB>){
	    print ALL $line;
	}
	close(SUB);
	close(ALL);
    }
    else{
	logOutput("No primer output for $subProjectDir",1);
    }
		
}

# If warning messages present, then save in .warnings.out file.
#
$OBJ_WARNINGS->createFile() if $OBJ_WARNINGS->getNumberOfWarnings;

exit 0;

#============================================================================#
# SUBROUTINES
#============================================================================#
sub getSubProjectDirectories {
    
    my $subProjectFile = shift;
    
    unless (open FH, $subProjectFile) {
        logError("ERROR: failed to open file $subProjectFile: $!",1);
    }
    chomp( my @subProjectDirs = <FH> );
    close FH;
    
    return @subProjectDirs;
    
}

#============================================================================#
sub run_primer3Design {
    my $inputFasta = $_[0];
    my $inputPrimerInfo = $_[1];
    my $outputPrimers = $_[2];
    my $primer3Params = $_[3];

    
    my $primer3Design = getScript("script.primer3Design");

    my $params .=  "-if $inputFasta " .
	           "-ic $inputPrimerInfo " . 
		   "-ip $primer3Params " . 
		   "-o  $outputPrimers " ;

    my $cmd = "$primer3Design $params";
    
    print STDERR $cmd if $DEBUG;

    my %processInfo = runCommand($cmd);
    
    checkProcess(%processInfo);
    
}
    
#============================================================================#
sub setFileForLogging {
    
    my $logFile = shift;
    
    $OBJ_LOGGER->setLogOutFileAppend($logFile);
    $OBJ_LOGGER->setLogErrorFileAppend($logFile);
    
}

#============================================================================#
sub getScript {
    
    my $scriptType = shift;
    
    my $script = $OBJ_PROPS->getProperty("$scriptType");
       $script = "$FindBin::RealBin/$script" if
            $script !~ /\//; # add path to script if not specified in config file
            
    # Check if script exists.
    #
    if ( ! checkForFileExistence($script) ) {
        my $errMsg = "ERROR: cannot find script $script defined in config file as $scriptType.\n";
        print $errMsg;
        logError($errMsg, 1);
    }
    
    return $script;
    
}

#============================================================================#
sub checkProcess {
    
    my %processInfo = @_;
    
    if ( $processInfo{exitCode} ) {
        my $errMsg = '';
        if ( defined $processInfo{logStdoutMessage} &&
            $processInfo{logStdoutMessage} ) {
            $errMsg .= $processInfo{stdoutMessage};
        }
        $errMsg .= $processInfo{stderrMessage};
        logError($errMsg);
    }
    
}

#============================================================================#
sub checkForFileExistence {
    
    my $file = shift;
    my $report = shift || 0;

    my $success = 1;
    
    if ( ! -s $file ) {
        my $errMsg = "$file file does not exist or is zero size.\n";
	print $errMsg if $report;
        logOutput($errMsg, 0);
        $success = 0;
    }
    
    return $success;
    
}

#============================================================================#
sub runCommand {
   
    my $cmd = shift;
    
    # Execute command, capture stderr, stdout, exitcode
    #
    my $errMessage = "CMD: $cmd\n";
    
    print "Running $cmd ...\n\n" if $DEBUG;
    logOutput($cmd);
    
    my %processInfo = runProcess($cmd,
        {-checkExecutable=>0,
        }
    );
    
    return %processInfo;
    
}

#============================================================================#
sub logExecution {
    
    my $programExecution = shift;
    
    my $msg = "Command: ".$programExecution."\n".
              "Current directory: ".getcwd;
    logOutput($msg);
}

#============================================================================#
sub logError {
    my $message = shift;
    my $confess = shift || 0;
    
    $OBJ_LOGGER->logError($message);
    $OBJ_WARNINGS->add($message);
    
    if ( $confess ) {
        $OBJ_WARNINGS->createFile() if $OBJ_WARNINGS->getNumberOfWarnings;
        confess $message;
    }
}
    
#============================================================================#
sub logOutput {
    my $message = shift;
    my $report = shift || 0;

    print $message if $report;
    $OBJ_LOGGER->logOut($message);
}
    
#============================================================================#
sub printhelp {
    my $verbose = shift || 1;
    pod2usage(-verbose=>$verbose);
    exit 1;
}

#============================================================================#
