#!/usr/bin/perl
use warnings;
use strict;
use Getopt::Long;
use File::Basename;

# Split up a 454 reads file into files based on how the reads were
#   assembled.  E.g., 'short', 'assembled', etc.
# Plus an 'Unused' category of all reads not in the status file.
# Plus an 'All' category for everything.
#
# Uses 454 software to do this.
# Rick Westerman, Purdue University, westerman@purdue.edu
#
# RPW.  Last mod Feb. 2010 to sort/uniq TrimStatus file. And to 
#   differentiate between various types of runs.
# RPW.  Mod March 2010 to add 'most' file for everything except unused. Aka,
#   'AllUsed.tfa'
# RPW.  Mod April 2010 to create a 'untrimmed' file that have everything but
#   not trimmed; can be used for quality control.

# Parameters 

my $help         = 0;
my @sff_files    = ('sff/*');
my $mid          = '';
my $status_file  = '454ReadStatus.txt';
my $trim_file    = '454TrimStatus.txt';
my $do_trim      = 1;
my $directory    = '.';
my $quiet        = 0;
my $verbose      = 0;
my $html         = 1;
my $delete_temp  = 1;
my $quality      = 1;

my $program_name = basename $0;

# List of types found in 454 files.  Make the most prevalant (or at least novel for that
#   particular run) read status the first array entry. 

my %types = ('assembly' => [qw (Assembled Outlier PartiallyAssembled Repeat Singleton TooShort)]
            ,'mapping'  => [qw (Unmapped  Full    Partial            Chimeric         TooShort)]
            );

my $run_type = 'assembly';          # Default
 
# 

GetOptions ('help!'         => \$help,
            'sff_files=s'   => \@sff_files,
            'mid=s'         => \$mid,
            'status_file=s' => \$status_file,
            'directory=s'   => \$directory,
            'quiet!'        => \$quiet,
            'verbose!'      => \$verbose,
            'quality!'      => \$quality,
            'html!'         => \$html,
            'delete_temp!'  => \$delete_temp,
           );

if ($help) {
    my %yn = (0 => '(default: NO)', 1 => '(default: Yes)' );

    my @h = (
        ' ',
        'Split up a 454 reads file into sub-files based on how the reads' ,
        '   were assembled or mapped. The sub-files will be named according',
        '   to the status of the reads; e.g., "TooShort.tfa"  ',
        ' ',
        '   Uses 454 software so must be run on pkr2 or venice',
        '---------------------------------------------------------------' ,
        ' ',
        '  --sff_file     ... SFF format file(s) with reads; can wildcard',
        '                       but put wildcard in single quotes. Can also',
        '                       have multiple commands. Can use mids.',
        '  --mid          ... If present add this mid to the front of each sff file',
        "  --status_file  ... File with read status [default: $status_file]",
        "  --trim_file    ... File with trim status [default: $trim_file]",
        "  --do_trim      ... If to do trimming from '--trim_file' $yn{$do_trim}",
        '  --directory    ... Directory in which to put the sub-files',
        '  --quality      ... If to also output quality files ' 
            . " $yn{$quality}",
        '  --quiet        ... Run quietly except for error messages'
            . " $yn{$quiet}",
        "  --verbose      ... For verbose messages $yn{$verbose}",
        '  --html         ... Create HTML partial page for output stats'
            . " $yn{$html}",
        "  --delete_temp  ... Delete temporary files $yn{$delete_temp}",
        '---------------------------------------------------------------' ,
        ' ',
        'Example:',
        "  $program_name --sff sff/* --status 454ReadStatus.txt --dir /home/westerm\n",
        'or:',
        "  $program_name --sff mid1\@04.sff --sff mid7\@03.sff\n", 
        'or:',
        "  $program_name --mid mid23     [to use default SFF directory and other files]\n",
        ' ',
        );

    my $s = join "\n", @h;
    print STDERR $s . "\n\n";
    exit;
}

# Look for a signature to see if we are on a 454-capable computer

my $test_result = `sfffile --help 2>&1`;

if (!defined $test_result || $test_result !~ /SFF/) {
    die "\n"
      . "  ****** Need to run this program on a machine with\n"
      . "         with 454 software; e.g., pkr2 or coates. Check for the\n"
      . "         program called 'sfffile'. ******* \n\n";
}


# Change parameters as needed and then check for validity

if ($mid ne '' && $mid !~ /\@/) {
    my $l = lc($mid);
    $mid = $l . '@';
}

$quiet = 1 if $verbose;

my @errors = ();

push @errors, '--status_file option required'  if length $status_file < 1;
push @errors, '--directory option required'    if length $directory < 1;

if ($#errors >= 0) {
    push @errors, '  Use --help for help';

    my $str = join "\n  ", @errors;
    die "\n  $str\n\n"
}

die "  Can not open status file '$status_file'\n" if !-r $status_file;

# Make up list of SFF files (e.g., expand the possible wild card). Make sure
#   file(s) are readable.  Make sure that the files are unique (via a hash)

my %actual_sff = ();
foreach my $sff (@sff_files) {
    foreach (glob($sff)) {
        $actual_sff{$_} = 1;
    }
} 
my @actual_sff = keys %actual_sff;

if ($#actual_sff < 0) {
    die "  No file(s) found for '$sff_files[0]'\n\n";
}

if ($verbose) {
    print "Looking at files:\n"
        . (join '  ', @actual_sff)
        . "\n";
}

foreach my $fn (@actual_sff) {
    die "  Can not read '$fn'\n\n" if !-r $fn;
}

# Find out what type of run was done via looking at the SFF files

foreach my $run (keys %types) {
    my $look_for = ${$types{$run}}[0];
    my $res = `grep $look_for $status_file | wc | cut -c 1-8`;
    chomp $res;
    $run_type = $run if $res != 0;
}

# Create directory if required

if (!-d $directory) {
    my $result = mkdir $directory;
    die "  Can not create directory '$directory' -- perhaps a read only"
      . " file system?\n\n" if !$result;
}

my $touch = "$directory/$program_name-test.tmp";
system ("touch $touch");
if (!-w $touch) {
    die "\n  ***** Problems writing to directory '$directory' -- perhaps"
      . " read only?\n\n";
}
system "rm $touch";

# Fix up trim file if needed

my $trim = '';
if ($do_trim) {
    system("grep -v Accno $trim_file | sort | uniq > $trim_file.tmp");
    $trim = "-t $trim_file.tmp";
}


# The remainder is rather simple since the 454 programs are called to do
#   the heavy work.  Perhaps better error checking could be done.

my $html_file = "$directory/partial.html";
if ($html) {
    my $cmd = "echo '<p>454 reads classified by type:\n<ul>' >$html_file";
    print STDERR $cmd . "\n" if $verbose;
    system $cmd ;
}

# 'Unused' takes special processing since it is exclusion of everything else

my $most_fname = "$directory/AllUsed.tfa";
my $most_qual  = "$most_fname.qual";
unlink $most_fname;
unlink $most_qual;

my $all_fname = "$directory/All.tfa";
my $all_qual  = "$all_fname.qual";
unlink $all_fname;
unlink $all_qual;

# 'Untrimmed is also special

my $untrimmed_fname = "$directory/Untrimmed.tfa";
unlink $untrimmed_fname;


# Get sequences for each run type

foreach my $type (@{$types{$run_type}}, 'Unused') {
    print "Processing '$type' reads \n" if not $quiet;

    my $cmd = $type eq 'Unused'
            ? "cp $status_file /tmp/$type.txt"
            : "fgrep '\t$type' $status_file > /tmp/$type.txt";

    print STDERR $cmd . "\n" if $verbose;
    system $cmd ;
    if (!-r "/tmp/$type.txt") {
        die "\n ***** Problem writing to '/tmp/$type.txt'\n";
    }

    if ($verbose) {
        system("echo -n 'Found: '; wc -l /tmp/$type.txt");
    }

    my $fname = "$directory/$type.tfa";
    my $qual  = "$fname.qual";
    unlink $fname;
    unlink $qual;

    foreach my $sff (@actual_sff) {
        my $param = $type eq 'Unused' ? '-e' : '-i';    # exclude or include

        $cmd = "sfffile -o /tmp/$type.sff $param /tmp/$type.txt $trim $mid$sff";
        print STDERR $cmd . "\n" if $verbose;
        system $cmd ;
        if (!-r "/tmp/$type.sff") {
            die "\n ***** sfffile program could not create file '/tmp/$type.sff''\n";
        }

        $cmd = "sffinfo -s /tmp/$type.sff >> $fname";
        print STDERR $cmd . "\n" if $verbose;
        system $cmd ;

        $cmd = "sffinfo -s /tmp/$type.sff >> $all_fname";
        print STDERR $cmd . "\n" if $verbose;
        system $cmd ;

        if ($type ne 'Unused') {
            $cmd = "sffinfo -s /tmp/$type.sff >> $most_fname";
            print STDERR $cmd . "\n" if $verbose;
            system $cmd ;
        }


        if ($quality) {
            $cmd = "sffinfo -q /tmp/$type.sff >> $qual";
            print STDERR $cmd . "\n" if $verbose;
            system $cmd ;

            $cmd = "sffinfo -q /tmp/$type.sff >> $all_qual";
            print STDERR $cmd . "\n" if $verbose;
            system $cmd ;
        
            if ($type ne 'Unused') {
                $cmd = "sffinfo -q /tmp/$type.sff >> $most_qual";
                print STDERR $cmd . "\n" if $verbose;
                system $cmd ;
            }
        }

        # This is not the ideal place to put the 'Untrimmed' file but
        #   at least the mid/sff information is captured here.  However must
        #   do this only once.  So am using the 'Unused' option.

        if ($type eq 'Unused') {
            $cmd = "sfffile -o /tmp/untrimmed.sff $mid$sff";
            print STDERR $cmd . "\n" if $verbose;
            system $cmd ;
            if (!-r "/tmp/$type.sff") {
                die "\n ***** sfffile program could not create file '/tmp/untrimmed.sff''\n";
            }
            $cmd = "sffinfo -seq -notrim /tmp/untrimmed.sff >> $untrimmed_fname";
            print STDERR $cmd . "\n" if $verbose;
                system $cmd ;
        }
            
    } # End of a sff file


    #
    # Contribute to the output table if requested
    #

    if ($html) {
        $cmd = "echo -n '<li><a href=\"$type.tfa\">$type</a> ['    >> $html_file; " 
             . " grep '>' $fname | wc | cut -c 1-8 | tr -d ' ' >> $html_file; "
             . " echo 'reads ]' >> $html_file "
             ;
        if ($quality) {
            $cmd .= "; echo -n '  <a href=\"$type.tfa.qual\">(Quality file)</a> ' >> $html_file" ;
        }
        system $cmd;
    }

    if ($delete_temp) {
        $cmd = "rm /tmp/$type.txt /tmp/$type.sff";
        print STDERR $cmd . "\n" if $verbose;
        system $cmd ;
    }
    
} # End of type

if ($html) {
    system "echo '</ul>' >> $html_file";
}

unlink "$trim_file.tmp" if $do_trim; 


# End of file
