#!/usr/bin/env perl
# PROJECT: CASAVA
# MODULE:  $RCSfile: splitUnSorted.pl,v $
# AUTHOR:  Richard Carter, Lukasz Szajkowski
#
# Copyright (c) 2008 Illumina
# This software is covered by the "Illumina Genome Analyzer Software
# License Agreement" and the "Illumina Source Code License Agreement",
# and certain third party copyright/licenses, and any user of this
# source file is bound by the terms therein (see accompanying files
# Illumina_Genome_Analyzer_Software_License_Agreement.pdf and
# Illumina_Source_Code_License_Agreement.pdf and third party
# copyright/license notices).
use warnings FATAL => 'all';
use strict;

use POSIX qw(strftime);

use IO::File;
use Sys::Hostname;
use Getopt::Long;

use lib '/home/psgendb/local/pkg/CASAVA_v1.8.2-build/lib/CASAVA-1.8.2/perl';
use Casava::Common::Log;
use Casava::Common::IOLib qw(executeCmd createDir);
use Casava::PostAlignment::Sequencing::Config qw(loadConfiguration %runsConfig
  %CONF_APP %CONF_PROJ %CONF_RUN isSpliceJunctionChrom readProjectParameters);
use Casava::PostAlignment::Sequencing::ExportToBam qw(sortedToBam);
use Casava::PostAlignment::Sequencing::RmDupAndSplit qw(rmDupAndSplitPairFiles);
use Casava::PostAlignment::Sequencing::SortLib qw(getConfiguredSortCmd sortFileList mergeFileList);


my $argvStr = join(' ', @ARGV);

my $usage =
    "splitUnSorted.pl [options]\n[options]\n"
  . "\t--binId - bin id\n"
  . "\t--chrom chromosome\n"
  . "\t--target which part of split should be run\n"
  . "\t--projectDir project directory\n";

my $projectDir  = "";
my $help        = 0;
my $binId       = 0;
my $chrom       = "";
my $target      = "";
my $result      = GetOptions(
	"binId=s"      => \$binId,
	"chrom=s"      => \$chrom,
	"projectDir=s" => \$projectDir,
	"target=s"     => \$target,
	"help|h"       => \$help
);

if (
	(
		   $result == 0
		|| $projectDir eq ""
		|| $chrom      eq ""
		|| $target     eq ""
		|| $binId      eq ""
	)
	&& $target ne "removeTemps"
	&& $target ne "mergeNMNMSingle"
  )
{
	errorExit "\n\n$usage";
}
loadConfiguration($projectDir);
#############################################################
# Configuration
my $cmd;
my $projectBinSize    = $CONF_PROJ{binSizeProject};
my $buildBinSize      = $CONF_PROJ{binSizeBuild};
my $timeStampFormat   = $CONF_APP{formatTimeStamp};
my $dateFormat        = $CONF_APP{formatDate};
my $dirToMove         = $CONF_APP{dirToMove};
my $sortByField       = 13;
my $now_string        = strftime( $dateFormat, localtime );
my $date              = $now_string;
my $hostname          = hostname();
my $isRemoveTemps     = ((defined $CONF_PROJ{removeTemps}) and ($CONF_PROJ{removeTemps} eq 'ON'));

my $dirBuildExport     = $CONF_PROJ{dirBuildExport};
my $dirBuildParsed     = $CONF_PROJ{dirBuildParsed};
my $dirBuildExportSets = File::Spec->catdir( $dirBuildExport, 'sets' );
my $projectBinDir      = File::Spec->catdir( $dirBuildExport , $chrom , $binId );
my $buildBinDir        = File::Spec->catdir( $dirBuildParsed , $chrom , $binId );

#############################################################
printLog(
	"Running $hostname:[$0 $argvStr] at "
	  . ( strftime $timeStampFormat, localtime ) . "\n",
	5
);
errorExit "ERROR: Could not find results dir $dirBuildParsed $!\n"
  unless ( -d $dirBuildParsed );

my $confDir = File::Spec->catdir($projectDir, $CONF_APP{dirConf});
my %projectChromsBinSizes = ();
readProjectParameters( %projectChromsBinSizes, "PROJECT_BIN_SIZES", $projectDir);
my @chroms = keys %projectChromsBinSizes;


if ( $target eq 'split' ) {
    my $incompleteFilePath = File::Spec->catfile( $projectBinDir , "sort.$target.incomplete" );
    $cmd = "touch '$incompleteFilePath'";
    executeCmd( $cmd, 5 );

    ### create directory and files structure ###
    #
    # Note that we do this without testing for the existance of moveDir
    # to help make the 'sort' module re-runable. If a previous build attempt
    # were interupted, the removeTemp target may have only partially 
    # deleted the directory structure rooted at moveDir, thus we always
    # do a complete rebuild:
    #
    my $moveDir = File::Spec->catdir( $projectBinDir , $dirToMove );
    createDir($moveDir);
    my $dirTarFilePath = File::Spec->catfile( $confDir , "dirs.tar" );
    $cmd = "tar -C '$moveDir' -xf '$dirTarFilePath'";
    executeCmd( $cmd, 5 );

    #
    # Process the unsorted exportpair files for the norm, orph and
    # anom cases to: (1) sort by pair start position (2) remove PCR
    # duplicates (3) split the paired read record into individual read
    # records, preserving the sorted state for a subset of the reads
    # where possible. No intermediate files are written out during
    # these steps, except temporaries used by the sort program.
    #
    rmDupAndSplitPairFiles($projectBinDir,$moveDir, $chrom, $binId);

    unlink($incompleteFilePath);
}    # if
     #-------------------------------------------------
     # THIS IS SYNCHRONIZATION POINT
     #-------------------------------------------------
elsif ( $target eq 'merge' ) {
    my $incompleteFilePath = File::Spec->catfile( $buildBinDir , "sort.$target.incomplete" );
    $cmd = "touch '$incompleteFilePath'";
    executeCmd( $cmd, 5 );

    #
    # first step is to sort all "wrong" files together for this bin
    #
    my @wrongFiles = ();
    for my $chromTmp (@chroms) {
        my $chromDirTmp     = File::Spec->catdir( $dirBuildExport , $chromTmp );
        my $maxNumberOfBins = $projectChromsBinSizes{$chromTmp};

        for ( my $i = 0 ; $i < $maxNumberOfBins ; $i++ ) {
            my $binDirTmp = sprintf "%04d", $i;
            my $wrongDir = File::Spec->catdir( $chromDirTmp,$binDirTmp,$dirToMove,$chrom,$binId );
            for my $filetag ( ( "", "anom_" ) ) {
                my $wrongFile = File::Spec->catfile( $wrongDir, $filetag . "wrong_export.txt" );
                push @wrongFiles, $wrongFile if (-e $wrongFile);
            }
        }
    }

    my @mergeFiles = ();
    my $sortWrongFile = File::Spec->catfile( $buildBinDir, "sort_wrong_export.txt" );

    sortFileList(@wrongFiles, $sortWrongFile,$sortByField,%CONF_PROJ,%CONF_APP);

    if($isRemoveTemps){
        my $cnt = unlink @wrongFiles;
        errorExit "ERROR: Failed to remove intermediate merge files from list: @wrongFiles\n" unless ($cnt == scalar(@wrongFiles));
    }

    if( scalar(@wrongFiles) > 0 ) {
        push @mergeFiles, $sortWrongFile;
    }

    #
    # find all remaining files to be merged into the sorted read file:
    #
    my $maxNumberOfBins = $projectChromsBinSizes{$chrom};

    for ( my $i = 0 ; $i < $maxNumberOfBins ; $i++ ) {
        my $binDirTmp = sprintf "%04d", $i;
        my $moveDirTmp = File::Spec->catdir( $dirBuildExport,$chrom,$binDirTmp,$dirToMove );
        for my $filetag ( ( "", "orph_", "anom_" ) ) {
            my $file = File::Spec->catfile( $moveDirTmp, "corect_sort_" . $filetag . $binId . "_export.txt");
            push @mergeFiles, $file if(-e $file);
        }
    }

    my $sortInfo = mergeFileList(@mergeFiles,'-',$sortByField,%CONF_PROJ,%CONF_APP);

    if(defined $sortInfo) {
        sortedToBam($projectDir,@chroms,$chrom,$binId,$sortInfo);
        $sortInfo = undef;
    }

    if($isRemoveTemps){
        my $cnt = unlink @mergeFiles;
        errorExit "ERROR: Failed to remove intermediate merge files from list: @mergeFiles\n" unless ($cnt == scalar(@mergeFiles));
    }

    unlink($incompleteFilePath);
}   # if



################################################################################
# merge single read bin
################################################################################
elsif ( $target eq 'mergeSingle' ) {
    if (not isSpliceJunctionChrom($chrom, %CONF_PROJ) ) {
	my $uFile = "single_export.txt";
	my @sortFiles;
	for my $i (1 .. scalar(@{$runsConfig{exportFiles}})) {
            my $uPath = File::Spec->catfile($dirBuildExportSets, $i,
                                            $chrom, $binId, $uFile );
            push @sortFiles, $uPath if(-e $uPath);
        }

        my $sortInfo = sortFileList(@sortFiles,'-',$sortByField,%CONF_PROJ,%CONF_APP);

        if(defined $sortInfo) {
            sortedToBam($projectDir,@chroms,$chrom,$binId,$sortInfo);
            $sortInfo = undef;
        }

        if($isRemoveTemps){
            my $cnt = unlink @sortFiles;
            errorExit("ERROR: Failed to remove intermediate sort files from list: @sortFiles\n") unless ($cnt == scalar(@sortFiles));
        }
    }
}    # if


################################################################################
# Remove all remaining temporary reads from duplicates and sort stage
################################################################################
elsif ( $target eq 'removeTemps' ) {
    # note this is never called for SE/RNA builds:
    my $baseDir = $dirBuildExport;
    for my $chromTmp (@chroms) {
        my $maxNumberOfBins = $projectChromsBinSizes{$chromTmp};
        for ( my $i = 0 ; $i < $maxNumberOfBins ; $i++ ) {
            my $binIdTmp = sprintf "%04d", $i;
            my $toMovePath = File::Spec->catfile( $baseDir, $chromTmp, $binIdTmp, $dirToMove );
            $cmd = "rm -fr $toMovePath";
            executeCmd( $cmd, 5 );
        }    # for
    }    # foreach
}    # if
else
{
    errorExit "ERROR: Unknown target: $target\n";
}

# if ( $target eq 'cleanMergeStarted' ) {
# 	if (
# 		!-e "$buildBinDir/sort.merge.finished"

# 		#&& -e "$buildBinDir/sort.merge.start"
# 	  )
# 	{
# 		$cmd = "rm -f $buildBinDir/sort.txt";
# 		executeCmd( $cmd, 5 );
# 	}
# }
# if ( $target eq 'cleanSplitStarted' ) {
# 	if (
# 		!-e "$projectBinDir/sort.merge.finished"

# 		#&& -e "$projectBinDir/sort.merge.start"
# 		&& -d "$projectBinDir/$dirToMove/"
# 	  )
# 	{
# 		$cmd =
# 		    "rm -fr $projectBinDir/sort.merge.start "
# 		  . "$projectBinDir/$dirToMove/";
# 		executeCmd( $cmd, 5 );
# 	}
# }


printLog( "Finished at " . ( strftime $timeStampFormat, localtime ) . "\n", 5 );
