#!/usr/bin/env perl

=head1 NAME

createAnchorTagsForAce.pl

=head1 SYNOPSIS

  createAnchorTagsForAce.pl [options] <454Scaffolds.txt> <gapDirsFile> <outputAnchorFile>
    
  Options:
  -a <file>          ace file (optional; if specified then ace file will be tagged)
  -log <file>        log file (optional; default is createAnchorTagsForAce.pl.log)
  -warn <file>       warnings log file (optional; default is defined in gapRes.config)
  -d                 Debug mode. Prints additional information in output file (optional)
  -h                 Detailed help message                                    (optional) 

=head1 DESCRIPTION

This software component is part of the Gap Resolution sub system that is
responsible for creating an output of tags that specify the coordinates of the 
anchors in the acefile.  The anchor tags are then added to the specified ace
file.

Unless otherwise noted, anchor here refers to the anchor sequence obtained
from the left and right contigs of the gap prior to reassembly.  This is done
upstream using idContigRepeats.pl.

The ouput of idRepeatBoundaries.pl is used, in conjunction with the size and
orientation of each contig within its scaffold, as specified in the 
454Scaffolds.txt file, to calculate the position of each anchor.
This information is then used to create a consed formatted output file of 
tags.

A default config file named gapRes.config residing in <installPath>/config is used 
to specify the following parameters:

(configurable)

  createAnchorTagsForAce.allowTransferOfTags
    Allow transfer of ace tags using transferConsensusTags (provided in 
    Phred/Phrap/Consed package).  Set to 1 for transferability, zero for 
    non-transferability.

  createAnchorTagsForAce.tagType
    Name of tag type when adding tag to ace file.

(system configuration)

  createGapResProject.assemInfoDirName
  idContigRepeats.scaffFileName
  idContigRepeats.boundaryFileExtension
  idRepeatBoundary.uniqueAnchorLength
  script.aceTagger

=head1 VERSION

$Revision: 1.11 $

$Date: 2010-01-13 00:26:00 $

=head1 AUTHOR(S)

Kurt M. LaButti

=head1 HISTORY

=over

=item *

Kurt M. LaButti 2009/15/07 creation

=item *

S.Trong 2009/08/05
  1. Refactored code to use gapRes.config file plus logging.
  2. Added ability to skip and create warnings file if sub project fails.

=item *

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

=item *

S.Trong 2010/01/12 - skip tag if start position is less than 1.

=back

=cut

use strict;
use warnings;
use Pod::Usage;
use Getopt::Long;
use Carp;
use Carp qw(cluck);
use Cwd;
use Cwd qw(abs_path);
use File::Path;
use File::Basename;
use FindBin qw($RealBin);
use lib "$RealBin/../lib";
use PGF::GapResolution::ParseScaffInfo;
use PGF::GapResolution::ParseBoundaryFile;
use PGF::GapResolution::Coords qw(reverseCompCoords);
use PGF::GapResolution::Warnings;
use PGF::Newbler::Scaffolds454;
use PGF::Utilities::Logger;
use PGF::Utilities::RunProcess qw(runProcess);
use vars qw($optHelp $optDebug $optAceFile $optLogFile $optWarningsFile );

#============================================================================#
# INPUT VALIDATION
#============================================================================#
my $programExecution = abs_path(dirname($0))."/".basename($0)." @ARGV";
 
if( !GetOptions(
                "a=s"  =>\$optAceFile,
                "log=s"=>\$optLogFile,
                "warn=s"=>\$optWarningsFile,
                "d"  =>\$optDebug,
                "h"  =>\$optHelp,
                )
    ) { 
    usage(); 
} 
 
usage(2) if $optHelp;
 
if ( @ARGV != 3 ) {
    print STDERR "Some arguments are missing.\n\n";
    usage(); 
} 

 
#============================================================================#
# INITIALIZE VARIABLES
#============================================================================#
my $scaffoldsFile  = shift @ARGV;
my $gapDirsFile = shift @ARGV;
my $anchorFile = shift @ARGV;

my $outputDir = getcwd;
my $DEBUG= $optDebug ? $optDebug : 0;
my $OBJ_454SCAFF =  PGF::Newbler::Scaffolds454->new($scaffoldsFile);
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 $OBJ_WARNINGS = PGF::GapResolution::Warnings->new(
    path=>$outputDir, logger=>$OBJ_LOGGER);
   $OBJ_WARNINGS->setLogFile($optWarningsFile) if $optWarningsFile;
my $OBJ_SCAFFINFO;
my $assemDir = $OBJ_PROPS->getProperty("createGapResProject.assemInfoDirName");
my $scaffInfoFileName = $OBJ_PROPS->getProperty("idContigRepeats.scaffFileName");

# If file names specified in config file doesn't contain paths, then
# prepend assemInfo path to it.
#
$gapDirsFile = "$assemDir/$gapDirsFile" unless $gapDirsFile =~ /\//;
$anchorFile = abs_path($anchorFile);

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

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

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

my $errMsg = '';

if (!$scaffoldsFile ) {
    $errMsg .= "Cannot find scaffold file $scaffoldsFile.\n";
}

if (!$gapDirsFile) {
    $errMsg .= "Cannot find file gap dir file $gapDirsFile.\n";
}

if ($optAceFile && !-s $optAceFile ) {
    $errMsg .= "The input ace file $optAceFile is missing or has zero size.\n";
}

if ( length $errMsg ) {
    print STDERR $errMsg;
    logError($errMsg,1);
}

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

#open dir list for reading
#
unless(open GAPDIRLIST, "$gapDirsFile") {
    logError("ERROR: Can't open file $gapDirsFile ($!)\n",1);
}
my @gapDirs = <GAPDIRLIST>;
close GAPDIRLIST;
    
my @anchorTags = ();

# find anchor tags
#
foreach my $dir (@gapDirs) {
    chomp $dir;
    next if !length $dir;
    
    # Add sub project dir name to warnings.
    #
    $OBJ_WARNINGS->setSubProjectName($dir);
    
    #path to scaffinfo file
    my $gapDir = basename($dir);
    my $scaffoldInfoFile = "$dir/$scaffInfoFileName";
    
    next if !$OBJ_WARNINGS->fileExists($scaffoldInfoFile);
        
    #scaffinfo object
    $OBJ_SCAFFINFO = PGF::GapResolution::ParseScaffInfo->new($scaffoldInfoFile);
    
    # read in gap dirs and grab info for each
    
    # Create anchor tags for each subproject
    #
    next if !createAnchorTags($dir, $scaffoldsFile, \@anchorTags );
}

# create anchor tag file
#
if ( @anchorTags ) {
    unless (open OFILE, ">$anchorFile") {
        logError("ERROR: failed to create output file ($anchorFile)($!)\n",1);
    }
    print OFILE @anchorTags;
    close OFILE;
}

# tag ace file if -a option is used.
#
if ( $OBJ_WARNINGS->fileExists($anchorFile) && $optAceFile ) {
    runAceTagger($optAceFile, $anchorFile);
}

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

exit 0;
    
#============================================================================#
# SUBROUTINES
#============================================================================#
sub createAnchorTags {
    my ($dir, $scaffoldsFile, $A_results) = @_;
    
    my $success = shift;
    my @outputs = ();
    my $boundaryFileExtension = 
        $OBJ_PROPS->getProperty("idContigRepeats.boundaryFileExtension");
    my $anchorSize = 
        $OBJ_PROPS->getProperty("idRepeatBoundary.uniqueAnchorLength");
    my $tagTransferability = 
        $OBJ_PROPS->getProperty("createAnchorTagsForAce.allowTransferOfTags");
    my $tagType = 
        $OBJ_PROPS->getProperty("createAnchorTagsForAce.tagType");
    my $script = basename($0);

    #path to scaffinfo file
    my $gapDir = basename($dir);
    
    #gap dir contig info
    my $leftContig     = $OBJ_SCAFFINFO->getLeftContig();
    my $leftContigLen  = $OBJ_SCAFFINFO->getLeftContigLength();
    my $rightContig    = $OBJ_SCAFFINFO->getRightContig();
    my $rightContigLen = $OBJ_SCAFFINFO->getRightContigLength();
    my $rightContigOrientation = $OBJ_454SCAFF->getContigOrientation($rightContig);
    my $leftContigOrientation  = $OBJ_454SCAFF->getContigOrientation($leftContig);
    
    #get anchor coordinates using the contig info
    my $rightBoundaryFile = "$dir/$leftContig"  . "$boundaryFileExtension";
    my $leftBoundaryFile  = "$dir/$rightContig" . "$boundaryFileExtension";
    
    $success = $OBJ_WARNINGS->fileExists($rightBoundaryFile);
    return 0 if !$success;
    $success = $OBJ_WARNINGS->fileExists($leftBoundaryFile);
    return 0 if !$success;
    
    my ($leftTagStart, $leftTagEnd)   = getAnchorCoords($rightBoundaryFile,
                                                        $leftContig,
                                                        $leftContigLen, 
                                                        $leftContigOrientation, 
                                                        "left",
                                                        $anchorSize);
    my ($rightTagStart, $rightTagEnd) = getAnchorCoords($leftBoundaryFile, 
                                                        $rightContig,
                                                        $rightContigLen, 
                                                        $rightContigOrientation, 
                                                        "right",
                                                        $anchorSize);
    
    if ( $leftTagStart > 0 ) {
        push @$A_results, "$leftContig\t$tagType\t$script\t$leftTagStart\t$leftTagEnd\t$tagTransferability\t${gapDir}_left\n";
    }
    if ( $rightTagStart > 0 ) {
        push @$A_results, "$rightContig\t$tagType\t$script\t$rightTagStart\t$rightTagEnd\t$tagTransferability\t${gapDir}_right\n";
    }
    
    return 1;
    
}

#============================================================================#
sub getAnchorCoords {
    my ($boundaryFile, $contig, $contigLen, $contigOri, $side, $anchorSize) = @_;
    my $tagStart;
    my $tagEnd;

  
    #grab info from file using the boundary object
    my $OBJ_BOUNDARY = PGF::GapResolution::ParseBoundaryFile->new($boundaryFile);
    my $uniqueStart = $OBJ_BOUNDARY->getUniqueStartPosition();
    my $uniqueEnd = $OBJ_BOUNDARY->getUniqueEndPosition();
    my $anchorStart = $OBJ_BOUNDARY->getAnchorStart($side, $anchorSize);
    my $anchorEnd = $OBJ_BOUNDARY->getAnchorEnd($side, $anchorSize);
    
    $tagStart = ($contigOri =~ /\-/) ? reverseCompCoords($contigLen, $anchorStart) : $anchorStart;  
    $tagEnd   = ($contigOri =~ /\-/) ? reverseCompCoords($contigLen, $anchorEnd) : $anchorEnd;  


    print "$boundaryFile $contig $contigLen $contigOri $side [$uniqueStart $uniqueEnd $anchorSize] $anchorStart $anchorEnd $tagStart $tagEnd\n" if $DEBUG;

 
    #if contig is in - orientation then the calculated start is actually 
    #the end and vice versa WITH RESPECT to contig in ace
    if ($contigOri =~ /\-/) {
	return ($tagEnd, $tagStart);
    } else {
	return ($tagStart, $tagEnd);
    }
        
}

#============================================================================#
sub runAceTagger {
    
    my $aceFile = shift;
    my $tagFile = shift;
    
    my $allowTransferOfTags = 
        $OBJ_PROPS->getProperty("createAnchorTagsForAce.allowTransferOfTags");
        
    my $script = getScript("script.aceTagger");
    my $cmdArgs = $allowTransferOfTags ? ' -trans' : ' -notrans';
       $cmdArgs =~ s/^\s+|\s+$//g;
    
    my $cmd = "$script $cmdArgs $tagFile $aceFile";
    my %processInfo = runCommand($cmd);
    
    checkProcess(%processInfo);
    
    logOutput($processInfo{stdoutMessage},1);
    
}
    
#============================================================================#
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 = "ERROR: cannot find script $script defined in config file as $scriptType.\n";
        print $errMsg;
        logError($errMsg, 1);
    }
    
    return $script;
    
}

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

#============================================================================#
sub setFileForLogging {
    
    my $logFile = shift;
    
    $OBJ_LOGGER->setLogOutFileAppend($logFile);
    $OBJ_LOGGER->setLogErrorFileAppend($logFile);
    
}

#============================================================================#
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" :
        "ERROR: failed to create $outputFile";
    logOutput($msg,1);
    
    return $fileExists;
    
}

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

#============================================================================#
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 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;
}

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