#!/usr/bin/env perl

=head1 NAME

createAnchorTagsForSubProjectAce.pl

=head1 SYNOPSIS

  createAnchorTagsForSubProjectAce.pl [options] <gapdirs.txt>

  Options:
  -log <file>  Log file (optional; default is createAnchorTagsForSubProjectAce.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 message (optional)

=head1 DESCRIPTION

This script adds anchor tags to all gap directories specified in the input.
It expects that the validation info file and Newbler ace file are found within
each subdirectory.  It will add anchor tags and also a custom navigation file 
to each subdirectory.


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


(system configuration)

  newblerAssemSubProject.newblerAssemblyDirectory

  validateSubProject.validateGapAssemblyOutputFile
	
  script.tagCTAce

=head1 VERSION

$Revision: 1.4 $

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

=head1 AUTHOR(S)

Brian Foster

=head1 HISTORY

=over

=item *

B.Foster 2009/07/24 creation

=item *

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

=item *

S.Trong 2009/12/29 - 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::Basename;
use FindBin qw($RealBin);
use lib "$RealBin/../lib";
use PGF::GapResolution::ParseGapValidationInfo;
use PGF::GapResolution::Warnings;
use PGF::Utilities::Properties;
use PGF::Utilities::RunProcess qw(runProcess);
use PGF::Utilities::Logger;
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,
                "d"  =>\$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 $outputDir = getcwd;
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 $logfile = $optLogFile ? $optLogFile : "$outputDir/".basename($0).".log";

my $subProjectListFile = $ARGV[0];
    
#============================================================================#
# VALIDATE INPUTS
#============================================================================#

my $tagTransferability = $OBJ_PROPS->getProperty(
    "createAnchorTagsForSubprojectAce.allowTransferOfTags");
my $tagType = $OBJ_PROPS->getProperty("createAnchorTagsForSubprojectAce.tagType");

my $errMsg = '';

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

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

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

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

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


# validate and get sub project directories.
#
my @subProjectDirs = validateSubProjectLocs($subProjectListFile);

foreach my $subProject (@subProjectDirs) {
    $subProject->{dir} =~ s/\/$//; # remove any trailing '/'
    
    # Add sub project dir name to warnings.
    #
    $OBJ_WARNINGS->setSubProjectName($subProject); 
 
    logOutput("\nCreating tags file for $subProject->{dir} ...",1);

    my $outputTagsFile = $subProject->{ace} . ".anchor.tags";
    my $outputNavFile = $subProject->{ace} . ".anchor.nav";
    open (TAGS, ">$outputTagsFile") or die "can't open $outputTagsFile\n";
    open (NAV, ">$outputNavFile") or die "can't open $outputNavFile\n";
    print NAV "TITLE: Anchors\n";

    my ($start,$end);
    if ($subProject->{leftContig}){
	
	$start = min([$subProject->{leftStart}, $subProject->{leftEnd}]);
	$end = max([$subProject->{leftStart}, $subProject->{leftEnd}]);

	print TAGS $subProject->{leftContig} . "\t$tagType\t" . basename($0) . "\t" .
	    "$start\t$end\t$tagTransferability\t" . 
	    basename ($subProject->{dir}) . "_left_" . $subProject->{leftContig} . "\n";
	
	print NAV "BEGIN_REGION\n";
	print NAV "TYPE: CONSENSUS\n";
	print NAV "CONTIG: " . $subProject->{leftContig} . "\n";
	print NAV "UNPADDED_CONS_POS: $start $end\n";
	print NAV "COMMENT: left_" . basename($subProject->{dir})  . "_left_" . $subProject->{leftContig} . "\n";
	print NAV "END_REGION\n\n";
    }
    if($subProject->{rightContig}){

	$start = min([$subProject->{rightStart}, $subProject->{rightEnd}]);
	$end = max([$subProject->{rightStart}, $subProject->{rightEnd}]);

	print TAGS $subProject->{rightContig} . "\t$tagType\t" . basename($0) . "\t" .
	    "$start\t$end\t$tagTransferability\t" . 
	    basename ($subProject->{dir}) . "_right_" . $subProject->{rightContig} . "\n";
	
	
	print NAV "BEGIN_REGION\n";
	print NAV "TYPE: CONSENSUS\n";
	print NAV "CONTIG: " . $subProject->{rightContig} . "\n";
	print NAV "UNPADDED_CONS_POS: " . $subProject->{rightStart} . " " . $subProject->{rightEnd} . "\n";
	print NAV "COMMENT: right_" . basename($subProject->{dir})  . "_right_" . $subProject->{rightContig} . "\n";
	print NAV "END_REGION\n\n";
    }
    close(TAGS);
    close(NAV);

    my $success = &runTagCTAce($outputTagsFile, $subProject->{ace});
    if ($success){
	logOutput("Tags created for $subProject->{dir} ...",1);
    }
    else{
	logError("Failed to tag acefile in $subProject->{dir} ...");
    }
}

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

exit 0;

#============================================================================#
# SUBROUTINES
#============================================================================#
sub validateSubProjectLocs {
    
    my $file = shift;   
    
    my @subProjectDirs = ();
     
    unless (open FH, $file ) {
        logError("ERROR: failed to open file $file: $!",1);
    }
    
    while( my $subProjectDir = <FH> ) {
        chomp $subProjectDir;
        my $errMsg = '';
	
	my $newblerAceFile=
	    "$subProjectDir/" . 
	    $OBJ_PROPS->getProperty("newblerAssemSubProject.newblerAssemblyDirectory") . 
	    "/assembly/consed/edit_dir/454Contigs.ace.1";

	my $validInfoFile = $subProjectDir.'/'.
	    $OBJ_PROPS->getProperty("validateSubProject.validateGapAssemblyOutputFile");
	
	my ($leftStart,$leftEnd,$rightStart,$rightEnd,$leftContig,$rightContig); 

	
        if ( !-e $subProjectDir ) {
            $errMsg = "ERROR: directory $subProjectDir defined in file $file does not exist.";
        }
	elsif ( !-s $newblerAceFile){
	    $errMsg = "ERROR: Newbler acefile $newblerAceFile not found.";
	}
	elsif ( !-s "$validInfoFile" ) {
            $errMsg = "ERROR: Valid Info file $validInfoFile file does notexist or is zero size.";
        }
	else{
	    my $objGapValidation = PGF::GapResolution::ParseGapValidationInfo->new($validInfoFile);
	    ($leftStart,$leftEnd) = $objGapValidation->getLeftAnchorPos();
	    ($rightStart,$rightEnd) = $objGapValidation->getRightAnchorPos();
	    $leftContig = $objGapValidation->getLeftContig();
	    $rightContig = $objGapValidation->getRightContig();
	}
	
        if ( $errMsg ){
            logError($errMsg, 0);
        } else {
            $subProjectDir =~ s/\/$//; # remove any trailing '/'
            push @subProjectDirs, {dir => $subProjectDir ,
				   leftStart => $leftStart , 
				   leftEnd => $leftEnd , 
				   rightStart => $rightStart, 
				   rightEnd => $rightEnd, 
				   leftContig => $leftContig, 
				   rightContig => $rightContig, 
				   ace =>$newblerAceFile};
        }
    }
    
    close FH;
    
    return @subProjectDirs;
    
}

#============================================================================#
sub runTagCTAce {
    
    my $tagsFile = shift;
    my $aceFile = shift;

    
    logOutput("Tagging ace file $aceFile ...", 1);
    
    my $success = 0;
    my $script = getScript("script.tagCTAce");

    my $cmd = "$script $tagsFile $aceFile";
    
    my %processInfo = runCommand($cmd);
    
    if ( checkProcess(%processInfo) ) {
        $success = 1;
    }
    
    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 = "ERROR: 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 min {return (sort {$a <=> $b} (@{$_[0]}))[0];}
#============================================================================#
sub max {return (sort {$b <=> $a} (@{$_[0]}))[0];}
#============================================================================#
sub printhelp {
    my $verbose = shift || 1;
    pod2usage(-verbose=>$verbose);
    exit 1;
}

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