#!/usr/bin/env perl

=head1 NAME

=head1 SYNOPSIS

  createSubProjectPCRPrimerInfo.pl [options] <validationSummary.txt>

  Options:
  -log <file>  Log file (optional; default is createSubProjectPCRPrimerInfo.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 sub system that is responsible
for creating a primer description file to be used for designing primers.

=head1 DEPENDENCIES

The input file contains information about the validation statuses for
each sub project directory. The format of the validationSummary.txt file are as
follows, one per line with each item delimited by a tab.

  1. sub project directory
  2. PASS|FAIL
  3. comment (optional)
  
Each sub project directory must contain a validinfo.txt file (previously
generated by validateSubProject.pl).  Note that this file contains the
validation status.  However, the status defined in the input validationSummary.txt
file supercedes the status within the validinfo.txt file. In other words, the
status in validationSummary is the one that is used to determine whether or not
to process the creation of fakes (status=pass) or to create primer files
(status=fail) for each sub project.

The status within the validationSummary.txt file determines the course of action.

If the validation failed and the anchors reside on different contigs,
create a primerinfo.txt file to be used for primer design (downstream process).

The following files must exist in each sub project directory:

  * validinfo.txt - create by validateSubProject.pl
  * 454AllContigs.fna - created by Newbler
  * 454AllContigs.qual - created by Newbler

The format of the primerinfo.txt file is as follows:
 
  FASTA_TAG=<fasta tag name>
  TARGET_REGION=<start> <length>
  TEMPLATE=GDNA
  LEFT_PRIMER_NAME=<name of left primer>
  RIGHT_PRIMER_NAME=<name of right primer>
  EXCLUDED_REGION=<start> <length>
      
  Note that the EXCLUDE_REGION key/value pair is optional. If used, more than one
  entry could be defined.
	       
A default config file named gapRes.config residing in 
<installPath>/config is used to specify the following parameters:

(configurable)

  createSubProjectPCRPrimerInfo.numberOfNsToInsertInGapConsensus=50
    If the validation fails and the anchors are on different contigs, a sequence
    of the left contig and the right contig will be produced with N paddings in
    the middle.  This parameter defines the number of N's to place between the
    two sequences.
    
  createSubProjectPCRPrimerInfo.maxDistanceAwayFromAnchorToDesignPrimer=1000
    Specify the maximum distance away from the anchor position to design primers.
    
(system configuration)

  createSubProjectPCRPrimerInfo.leftPrimerNameExtension=_left
    Specify the suffix for naming the left primer within the primers file.
    
  createSubProjectPCRPrimerInfo.rightPrimerNameExtension=_right
    Specify the suffix for naming the right primer within the primers file.
    
  createSubProjectPCRPrimerInfo.primerInfoFile=primerinfo.txt
    Specify the name of the file containing the primer reactions.
    
  createSubProjectPCRPrimerInfo.gapFastaFileExtension=.gap.fasta
    Specify the file extension for the fasta file containing the gap concensus.
    
  createSubProjectPCRPrimerInfo.primerInfoTemplateName=GDNA
    Sepcify the template name in the primers file
    
=head1 VERSION

$Revision: 1.2 $

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

=head1 AUTHOR(S)

Stephan Trong

=head1 HISTORY

=over

=item *

S.Trong 2009/10/20 creation

=item *

S.Trong 2009/10/20 - added -log and -warn option.

=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::RunProcess qw(runProcess);
use PGF::Utilities::Properties;
use PGF::GapResolution::ParseGapValidationInfo;
use PGF::GapResolution::ParseValidationSummary;
use PGF::GapResolution::Warnings;
use PGF::Parsers::FastaParse;
use PGF::Utilities::Logger;
use PGF::Utilities::FastaSeq qw(formatSeq);
use vars qw( $optHelp $optDebug $optLogFile $optWarningsFile );

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

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

printhelp(2) if $optHelp;

if ( @ARGV != 1 ) {
    my $errMsg = "Required input parameters are missing in command line.";
    print STDERR "$errMsg\n";
    printhelp(1);
}

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

my $DEBUG = $optDebug ? 1:0;
my $configFile = defined $ENV{GAPRES_CONFIG} ?
    $ENV{GAPRES_CONFIG} : "$RealBin/../config/gapRes.config";
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 $outputDir = getcwd;
my $OBJ_WARNINGS = PGF::GapResolution::Warnings->new(
    path=>$outputDir, logger=>$OBJ_LOGGER);
   $OBJ_WARNINGS->setLogFile($optWarningsFile) if $optWarningsFile;
my $logfile = $optLogFile ? $optLogFile : "$outputDir/".basename($0).".log";

my $validationSummaryFile = $ARGV[0];
    
#============================================================================#
# VALIDATE INPUTS
#============================================================================#
my $errMsg = '';

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

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

# Check sub project list file.
#
if (!-s $validationSummaryFile) {
    $errMsg .= "The $validationSummaryFile is missing or zero size.\n";
}

if ( $errMsg  ) {
    print STDERR "$errMsg.\nUse the -h option for more info.\n";
    logError($errMsg,1);
}

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

my @validationStatuses = ();

# validate and get sub project directories.
#
my $objValidation = PGF::GapResolution::ParseValidationSummary->new(
    $validationSummaryFile);

while ( $objValidation->getNextEntry ) {
    my $subProjectDir = $objValidation->getPath;
    next if !length $subProjectDir;
    
    if ( !-s $subProjectDir ) {
        my $errMsg = "in file $validationSummaryFile, directory $subProjectDir does not exist.\n";
        print STDERR "WARNING: $errMsg";
        logError($errMsg);
        next;
    }
    
    # Check if validation is successful.  If not, then sub project is
    # a candidate for primer design.  If successful, a fake should've been
    # created, so skip to the next sub project.
    #
    next if $objValidation->isSuccessful;
    
    # Add sub project dir name to warnings.
    #
    $OBJ_WARNINGS->setSubProjectName($subProjectDir);

    # Initialize file name and paths
    #
    my $subProjectName = basename($subProjectDir);
    my $assemDir = $subProjectDir.'/'.
        $OBJ_PROPS->getProperty("validateSubProject.assemblyDirectory");
    my $validInfoFile = $subProjectDir.'/'.
        $OBJ_PROPS->getProperty("validateSubProject.validateGapAssemblyOutputFile");
    my $contigsFasta = $assemDir.'/'.
        $OBJ_PROPS->getProperty("validateSubProject.assemblyContigsFasta");
    my $contigsQual= $assemDir.'/'.
        $OBJ_PROPS->getProperty("validateSubProject.assemblyContigsQual");
        
    # check for existence of required files
    #
    next if !checkForRequiredFiles(
        $validInfoFile,
        $contigsFasta,
        $contigsQual,
    );
    
    # Create consensus fasta and qual files of the sub project.
    #
    my $success = createOutputFiles($subProjectDir, $validInfoFile,
        $contigsFasta, $contigsQual);
    next if !$success;
}

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

exit 0;

#============================================================================#
# SUBROUTINES
#============================================================================#
sub checkForRequiredFiles {
    
    my @files = @_;
    
    my $success = 1;
    
    foreach my $file (@files) {
        if ( !$OBJ_WARNINGS->fileExists($file) ) {
            $success = 0;
            last;
        }
    }
    
    return $success;
}

#============================================================================#
sub createContigOrientationFile {
    
    my $scaffoldFile = shift;
    my $outputFile = shift;
    
    logOutput("Creating contig orientation file ...", 1);
    
    my $objScaffold = PGF::Newbler::Scaffolds454->new($scaffoldFile);
    $objScaffold->createContigOrientationFile($outputFile);
    
    my $success = $OBJ_WARNINGS->fileExists($outputFile);
    
    logOutputFileCreation($outputFile);
    
    return $success;
    
}

#============================================================================#
sub createOutputFiles {
    
    my $subProjectDir = shift;
    my $validationInfoFile = shift;
    my $contigsFasta = shift;
    my $contigsQual = shift;
    
    my $success = 1;
    my $objGapValidation = PGF::GapResolution::ParseGapValidationInfo->new(
        $validationInfoFile);
    
    # Get contig where left anchor is found.
    #
    my $leftAnchorContig = $objGapValidation->getLeftContig();
    if ( !$leftAnchorContig ) {
        my $errMsg = "failed to parse left anchor contig from file $validationInfoFile\n";
        logError($errMsg);
        $success = 0;
    }
    
    # Get contig where right anchor is found.
    #
    my $rightAnchorContig = $objGapValidation->getRightContig();
    if ( !$rightAnchorContig ) {
        my $errMsg ="failed to parse right anchor contig from file $validationInfoFile\n";
        logError($errMsg);
        $success = 0;
    }
    
    # If both anchors are on the same contig and validation is successful,
    # then create fakes.
    #
    if ( $success && $objGapValidation->doPrimerDesign ) {
        # If validation failed and anchors are on different contigs, merge contig
        # sequences with N's in the middle and create primer file for design
        # primers.
        #
        my $outputFile = $subProjectDir.'/'.basename($subProjectDir).
            $OBJ_PROPS->getProperty("createSubProjectPCRPrimerInfo.gapFastaFileExtension");
        $success = createGapConsensus($objGapValidation, $subProjectDir,
            $leftAnchorContig, $rightAnchorContig, $contigsFasta, $outputFile);
    }
    
    return $success;
    
}

#============================================================================#
sub createGapConsensus {
    
    my $objGapValidation = shift;
    my $subProjectDir = shift;
    my $leftAnchorContig = shift;
    my $rightAnchorContig = shift;
    my $contigsFasta = shift;
    my $outputFile = shift;
    
    my ($leftAnchorStart,$leftAnchorEnd) = $objGapValidation->getLeftAnchorPos();
    my ($rightAnchorStart,$rightAnchorEnd) = $objGapValidation->getRightAnchorPos();
    my $leftContigLength = $objGapValidation->getLeftContigLength();
    my $rightContigLength = $objGapValidation->getRightContigLength();
    my $numberOfNsInGapConsensus = $OBJ_PROPS->getProperty(
        "createSubProjectPCRPrimerInfo.numberOfNsToInsertInGapConsensus");
    my $nBases = 'N' x $numberOfNsInGapConsensus;
    my $gapLength = length($nBases);
    
    # Get left and right contig sequences, including correct orientation
    # and layout.
    #
    my ($leftContigSeq, $rightContigSeq) = getAnchorContigSeqs($leftAnchorContig,
        $rightAnchorContig, $leftAnchorStart, $leftAnchorEnd, $rightAnchorStart,
        $rightAnchorEnd, $contigsFasta);
    
    # Create sequence of the two gap contigs + N's in place of the gap.
    #
    my $fastaTag = basename($subProjectDir);
    my $fastaTagWithComment = "$fastaTag $leftAnchorContig $rightAnchorContig";
    my $seq = $leftContigSeq.$nBases.$rightContigSeq;
       $seq = formatSeq($seq, 50);
    
    # Create fasta file containing the two contigs and the gap represented
    # as Ns.
    #
    my $success = createFastaFile($outputFile, $fastaTagWithComment, $seq);
    
    # Create primer info file.
    #
    if ( $success ) {
        # Get regions to exclude from designing primers.
        #
        my @excludedRegions = getExcludedRegions($leftAnchorStart, $leftAnchorEnd,
            $rightAnchorStart, $rightAnchorEnd, $leftContigLength, $rightContigLength,
            $gapLength);
        
        # Create primer info file.
        #
        my $targetRegionStart = $leftContigLength + 1;
        my $targetRegionEnd = $leftContigLength + $gapLength;
        my $targetRegionLength = $targetRegionEnd - $targetRegionStart + 1;
        my $primerFile = $subProjectDir.'/'.$OBJ_PROPS->getProperty(
                "createSubProjectPCRPrimerInfo.primerInfoFile");
        
        $success = createPrimerInfoFile(
            fastaTag=>$fastaTag,
            targetRegionStart=> $targetRegionStart,
            targetRegionLength=>$targetRegionLength,
            template=>$OBJ_PROPS->getProperty("createSubProjectPCRPrimerInfo.primerInfoTemplateName"),
            leftPrimerName=>$fastaTag.$OBJ_PROPS->getProperty(
                "createSubProjectPCRPrimerInfo.leftPrimerNameExtension"),
            rightPrimerName=>$fastaTag.$OBJ_PROPS->getProperty(
                "createSubProjectPCRPrimerInfo.rightPrimerNameExtension"),
            excludedRegions=>\@excludedRegions,
            outputFile=>$primerFile,
        );
    }
        
    return $success;
    
}

#============================================================================#
sub getAnchorContigSeqs {
    
    my $leftAnchorContig = shift;
    my $rightAnchorContig = shift;
    my $leftAnchorStart = shift;
    my $leftAnchorEnd = shift;
    my $rightAnchorStart = shift;
    my $rightAnchorEnd = shift;
    my $contigsFasta = shift;
    
    my $leftContig = $leftAnchorContig;
    my $rightContig = $rightAnchorContig;
    
    # Get anchor orientation, returns either '+' or '-'.
    #
    my $leftAnchorOrientation = getAnchorOrientation($leftAnchorStart,
        $leftAnchorEnd);
    my $rightAnchorOrientation = getAnchorOrientation($rightAnchorStart,
        $rightAnchorEnd);
    
    # Get contig sequences.
    #
    my $leftContigSeq = getConsensusSeq($leftContig, $leftAnchorOrientation,
        $contigsFasta);
    my $rightContigSeq = getConsensusSeq($rightContig, $rightAnchorOrientation,
        $contigsFasta);
    
    my $errMsg = '';
    if ( !length $leftContigSeq ) {
        $errMsg = "failed to parse left contig sequence $leftAnchorContig from $contigsFasta\n";
    }
    if ( !length $rightContigSeq ) {
        $errMsg .= "failed to parse right contig sequence $rightAnchorContig from $contigsFasta\n";
    }
    if ( $errMsg ) {
        cluck "$errMsg";
        logError($errMsg);
    }
    
    return $leftContigSeq, $rightContigSeq;
    
}

#============================================================================#
sub getAnchorOrientation {
    
    my $start = shift;
    my $end = shift;
    
    my $orientation = '';
    
    if ( $start <= $end ) {
        $orientation = '+';
    } else {
        $orientation = '-';
    }
    
    return $orientation;
    
}

#============================================================================#
sub getExcludedRegions {
    
    my $leftAnchorStart = shift;
    my $leftAnchorEnd = shift;
    my $rightAnchorStart = shift;
    my $rightAnchorEnd = shift;
    my $leftContigLength = shift;
    my $rightContigLength = shift;
    my $gapLength = shift;
    
    my @excludedRegions = ();
    my $maxDistanceAwayFromAnchorToDesignPrimer = $OBJ_PROPS->getProperty(
        "createSubProjectPCRPrimerInfo.maxDistanceAwayFromAnchorToDesignPrimer");
    # note that max distance from anchor does not include the anchor itself.
    
    # Get anchor orientation, returns either '+' or '-'.
    #
    my $leftAnchorOrientation = getAnchorOrientation($leftAnchorStart,
        $leftAnchorEnd);
    my $rightAnchorOrientation = getAnchorOrientation($rightAnchorStart,
        $rightAnchorEnd);

    if ( $leftAnchorOrientation eq '-' ) {
        ($leftAnchorStart, $leftAnchorEnd) = reOrientAnchorPos($leftAnchorStart,
            $leftAnchorEnd, $leftContigLength);
    }
    if ( $rightAnchorOrientation eq '-' ) {
        ($rightAnchorStart, $rightAnchorEnd) = reOrientAnchorPos($rightAnchorStart,
            $rightAnchorEnd, $rightContigLength);
    }
    
    # left anchor: start --> end
    # right anchor: start --> end
    
    #                           exclude include exclude
    #                                | |       |        |
    #        --------------------N..N--------------------
    # left   |      |      -->  |      -->              right
    # anchor |      |         | |                       anchor
    #        exclude  include  exclude
    #
    # "include" represents the maxDistanceAwayFromAnchorToDesignPrimer + the
    # anchor length.
    #
    my $excludeLeftAnchorLeftStart = 1;
    my $excludeLeftAnchorLeftEnd = $leftAnchorStart - $maxDistanceAwayFromAnchorToDesignPrimer - 1;
       $excludeLeftAnchorLeftEnd = 1 if $excludeLeftAnchorLeftEnd < 1;
       $excludeLeftAnchorLeftEnd = $leftAnchorStart - 1 if $excludeLeftAnchorLeftEnd >= $leftAnchorStart;
    my $excludeLeftAnchorLeftLength = $excludeLeftAnchorLeftEnd - $excludeLeftAnchorLeftStart + 1;
       $excludeLeftAnchorLeftLength = 0 if $excludeLeftAnchorLeftLength < 0;
    
    my $excludeLeftAnchorRightStart = $leftAnchorEnd + 1;
    my $excludeLeftAnchorRightEnd = $leftContigLength;
    my $excludeLeftAnchorRightLength = $excludeLeftAnchorRightEnd - $excludeLeftAnchorRightStart + 1;
       $excludeLeftAnchorRightLength = 0 if $excludeLeftAnchorRightLength < 0;
    
    my $excludeRightAnchorLeftStart = $leftContigLength + $gapLength + 1;
    my $excludeRightAnchorLeftEnd = $leftContigLength + $gapLength + 1 + $rightAnchorStart - 1;
    my $excludeRightAnchorLeftLength = $rightAnchorStart - 1;
       $excludeRightAnchorLeftLength = 0 if $excludeRightAnchorLeftLength < 0;
    
    my $excludeRightAnchorRightStart = $leftContigLength + $gapLength + $rightAnchorEnd +
        $maxDistanceAwayFromAnchorToDesignPrimer + 1;
    my $excludeRightAnchorRightEnd = $leftContigLength + $gapLength + $rightContigLength;
    my $excludeRightAnchorRightLength = $excludeRightAnchorRightEnd - $excludeRightAnchorRightStart + 1;
       $excludeRightAnchorRightLength = 0 if $excludeRightAnchorRightLength < 0;
    
    push @excludedRegions, [ $excludeLeftAnchorLeftStart, $excludeLeftAnchorLeftLength ];
    push @excludedRegions, [ $excludeLeftAnchorRightStart, $excludeLeftAnchorRightLength ];
    push @excludedRegions, [ $excludeRightAnchorLeftStart, $excludeRightAnchorLeftLength ];
    push @excludedRegions, [ $excludeRightAnchorRightStart, $excludeRightAnchorRightLength ];
        
    return @excludedRegions;
    
}

#============================================================================#
sub reOrientAnchorPos {
    
    my $start = shift;
    my $end = shift;
    my $contigLength = shift;
    
    my $newStart = $contigLength - $start;
    my $newEnd =  $contigLength - $end + 1;
    
    return $newStart, $newEnd;
    
}

#============================================================================#
sub createPrimerInfoFile {
    
    my %params = @_;
    
    my $outputFile = $params{outputFile};
    unless (open FH, ">$outputFile" ) {
        logError("failed to create file $outputFile: $!\n",1);
    }
    
    print FH "FASTA_TAG=$params{fastaTag}\n";
    print FH "TARGET_REGION=$params{targetRegionStart} $params{targetRegionLength}\n";
    print FH "TEMPLATE=$params{template}\n";
    print FH "LEFT_PRIMER_NAME=$params{leftPrimerName}\n";
    print FH "RIGHT_PRIMER_NAME=$params{rightPrimerName}\n";
    
    if ( @{$params{excludedRegions}} ) {
        foreach my $refExcludeRegion ( @{$params{excludedRegions}} ) {
            print FH "EXCLUDED_REGION=$refExcludeRegion->[0] $refExcludeRegion->[1]\n";
        }
    }
    
    close FH;
    
    my $success = $OBJ_WARNINGS->fileExists($outputFile);
    
    logOutputFileCreation($outputFile);
    
    return $success;
    
}

#============================================================================#
sub getConsensusSeq {
    
    my $contig = shift;
    my $contigOrientation = shift;
    my $fastaFile = shift;
    my $rawFormat = shift || 0;
    
    my $seq = '';
    my $defLine = '';
    my $objFastaParser = PGF::Parsers::FastaParse->new($fastaFile);
    
    while ($objFastaParser->MoreEntries) {
        $objFastaParser->ReadNextEntry( -rawFormat=>$rawFormat );
        my $tagName = $objFastaParser->Name;
        if ( $tagName eq $contig ) {
            $objFastaParser->reverseComp if $contigOrientation eq '-';
            $seq = $objFastaParser->Seq;
            $defLine = $objFastaParser->Comment;
            last;
        }
    }
    $seq =~ s/^\s+|\s+$//g;
    
    if ( wantarray ) {
        return $seq, $defLine;
    } else {
        return $seq;
    }
}

#============================================================================#
sub createFastaFile {
    
    my $file = shift;
    my $header = shift;
    my $seq = shift;
    
    my $success = 0;
    
    unless (open FH, ">$file" ) {
        my $errMsg = "failed to create file $file: $!\n";
        print STDERR "$errMsg";
        logError($errMsg);
        return $success;
    }
    print FH ">$header\n";
    print FH "$seq\n";
    close FH;
    
    $success = $OBJ_WARNINGS->fileExists($file);
    
    logOutputFileCreation($file);
    
    return $success;
}

#============================================================================#
sub checkForFileExistence {
    
    my $file = shift;
    
    my $success = 1;
    
    if ( !-s $file ) {
        my $errMsg = "file $file does not exist or is zero size.\n";
        logError($errMsg, 0);
        $success = 0;
    }
    
    return $success;
    
}

#============================================================================#
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 ( !-e $script ) {
        my $errMsg = "cannot find script $script defined in config file as $scriptType.\n";
        print $errMsg;
        logError($errMsg, 1);
    }
    
    return $script;
    
}

#============================================================================#
sub checkProcess {
    
    my %processInfo = @_;
    
    my $success = 1;
    
    if ( $processInfo{exitCode} ) {
        my $errMsg = '';
        if ( defined $processInfo{logStdoutMessage} &&
            $processInfo{logStdoutMessage} ) {
            $errMsg .= $processInfo{stdoutMessage};
        }
        $errMsg .= $processInfo{stderrMessage}."\n";
        print STDERR "$errMsg\n";
        logError($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";
    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 $printMsg = shift || 0;
    
    $OBJ_LOGGER->logOut($message);
    print "$message\n" if $printMsg;
}
    
#============================================================================#
sub logOutputFileCreation {
    
    my $outputFile = shift;
    
    $outputFile = abs_path($outputFile);
    
    my $fileExists = -s $outputFile ? 1:0;
    my $msg = $fileExists ? "Created $outputFile" :
        "failed to create $outputFile";
    logOutput($msg,1);
    
    return $fileExists;
    
}

#============================================================================#
sub printhelp {
    my $verbose = shift || 1;
    pod2usage(-verbose=>$verbose);
    exit 1;
}

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