#!/usr/bin/env perl
################################################################################
#677968693279877869823280736982826932696779678132535748555051505432666947666976#
#some modules required (if not installed use command line cpan install <module>)
use Modern::Perl '2011'; # used as warnings/strict
use Smart::Comments '###'; # used to trace run progress 
use Getopt::Euclid;# used to document perl code
use File::Basename; # used to capture file names and locations
use File::Copy 'copy'; # used to archive some files
use File::Compare; # used to compare files (archiving)
use Cwd; # used to catch current working directory
use File::Find; # used to recursively locate files / folders
use Win32::DriveInfo;#to check drive space available
use Win32::OLE;# used to populate MS-Excel Workbooks
use Win32::GUI;# used to create Explorer Windows; will fail end of install as designed for 32 bit
#next is required to upgrade for 64 bit windows (from command line)
#ppm upgrade -install Win32::GUI
no warnings 'File::Find';
################################################################################
#677968693279877869823280736982826932696779678132535748555051505432666947666976#
my $code = $0;#codename
my $codedir = cwd($0);# code location; that's where everything should be located
my $currentdir = getcwd();
my $codename = $0;
if ($codename =~ m/([^\/]+)$/) {#for archiving
	$codename = $1;}
my $rundatetime = getdate();# get date_time from sub for reporting
print "\n\n$rundatetime:Welcome to $codename running from $codedir\n";
################################################################################
#677968693279877869823280736982826932696779678132535748555051505432666947666976#
#fetching folders to search for FastQ.gz
print "\n\nYou are going to be asked providing code with:";
print "\n\tThe location (folder or even drive) of 7-Zip executable (named 7z.exe)";
print "\n\tSome location(s) = folder(s) to catch FastQ.gz file(s)";
print "\n\tAn existing location to record selected decompressed FastQ.gz file(s)";
print "\n\tAn existing location to archive selected FastQ.gz file(s)";
print "\n\nIf you are ok with all of these, just type anything otherwise use Ctrl-C then use perl <code> --man\n";
my $goforit =<STDIN>;
#first the 7-Zip executable (first attemp to locate it automatically from a given drive)
my $sevenzipexe;
SEVENZIP:
while (1 > 0) {
	my $sevenzipfolder = Win32::GUI::BrowseForFolder(
        -folderonly => 1,
        -title => "Select the folder(drive) from which the 7-Zip executable (7z.exe) can be found",);
    while($sevenzipfolder =~ s/\//\\/){}
	while($sevenzipfolder =~ s/\'//) {}
	while($sevenzipfolder =~ s/\"//) {}
    if ((-e $sevenzipfolder) && (-d $sevenzipfolder)) {
		find(\&search7zip, "$sevenzipfolder");
		if (defined($sevenzipexe)) {
			last SEVENZIP;}
		else {
			print "\nSorry cannot find 7Zip executable from the folder $sevenzipfolder.\nHave Ctrl-C to quit, anything else to try again.\n";
			my $answer = <STDIN>;}}
	else {
		print "\nSorry cannot find the folder you provided for 7Zip executable ($sevenzipfolder).\nHave Ctrl-C to quit, anything else to try again.\n";
			my $answer = <STDIN>;}}
print "\nPutative 7-Zip executable ok ($sevenzipexe). Let's go for FastQ.gz files\n\n";
sleep 3;
################################################################################
#677968693279877869823280736982826932696779678132535748555051505432666947666976#
my @fastqgzfinalarr;
my @fastqgzarr;
my $fastqgzsize = 0;
my $fastqsize = 0;
my $fastqgzfastqfactor = 5;#approx
my $sizewarningfactor = 0.95;#arbitrary
my $sizestopfactor = 0.975;#arbitrary
FASTQGZ:
while(1>0) {
	my $folder = Win32::GUI::BrowseForFolder(
#        -includefiles => 1,
        -folderonly => 1,
        -title => "Select a FastQ.gz source folder (click Cancel to stop selecting)",);
    if (not defined($folder)) {
		if (not defined($fastqgzfinalarr[0][0])) {
			print "\nWarning: You need to select one valid FastQ.gz source folder.\nHave Ctrl-C to quit, anything else to try again.\n";
			my $answer = <STDIN>;}
		else {
			last FASTQGZ;}}
	elsif ((-e $folder) & (-d $folder)) {
		#so checking for FastQ.gz files under this location (and starting to reformat folder name for windows)
		undef @fastqgzarr;
		while($folder =~ s/\//\\/){}
		while($folder =~ s/\'//) {}
		while($folder =~ s/\"//) {}
		find(\&recordentry, $folder);
		if (not defined($fastqgzarr[0][0])) {
			print "\nSorry they are no .fastq.gz file in this folder.\nHave Ctrl-C to quit, anything else to try again.\n";
			my $answer = <STDIN>;}
		else {
			#checking for duplicates and showing fastq.gz that will be processed
			#also checking for size (and expected decompressed size)
			my $kc1 = 1;
			while ($kc1 <= $fastqgzarr[0][0]) {
				my $kc2 = 1;
				my $founddone = "NEW FILE";
				FASTQGZFINAL:
				while (defined($fastqgzfinalarr[$kc2][1])) {
					if ($fastqgzfinalarr[$kc2][1] eq $fastqgzarr[$kc1][1]) {
						$founddone = "DUPLICATE";
						print "DUPLICATE of $fastqgzarr[$kc1][1]\n";
						last FASTQGZFINAL;}
					++$kc2;}
				if ($founddone eq "NEW FILE") {
					$fastqgzfinalarr[++$fastqgzfinalarr[0][0]][1] = $fastqgzarr[$kc1][1];
					$fastqgzfinalarr[$fastqgzfinalarr[0][0]][2] = $fastqgzarr[$kc1][2];
					if ($fastqgzarr[$kc1][1] =~ m/^(.+)\.GZ$/i) {
						$fastqgzfinalarr[$fastqgzfinalarr[0][0]][3] = $1;}
					my $size = -s $fastqgzarr[$kc1][2];
					$size = sprintf("%.0f", ($size/1000000));
					$fastqgzsize = $fastqgzsize + $size;
					$fastqsize = $fastqsize + $size * $fastqgzfastqfactor;
					print "NEW FILE for $fastqgzarr[$kc1][1] (size .gz = $size mb)\n";}
				++$kc1;}}}
	else {
		print "\nWarning: Cannot find proposed folder or is not a folder.\nHave Ctrl-C to quit, anything else to to try again.\n";
		my $answer = <STDIN>;}
	sleep 3;}		
print "\nSelected $fastqgzfinalarr[0][0] FastQ.gz file(s) from proposed folder(s).\n";
print "Size of FastQ.gz = $fastqgzsize mb and estimated Size of FastQ = $fastqsize mb\n";
################################################################################
#677968693279877869823280736982826932696779678132535748555051505432666947666976#
#output folder for fastq files
my $outfastqdir;
my @existingfastqarr;
my $outfastqdrive;
my $outfastqdrivesize;
my $outfastqdriveroom;
OUTFASTQDIR:
while(1>0) {
	$outfastqdir = Win32::GUI::BrowseForFolder(
		-editbox => 1,
        -folderonly => 1,
        -title => "Select an existing output folder for decompressed FastQ.gz",);
    if (not defined($outfastqdir)) {
		print "\nWarning: You need to select one existing output folder for decompressed FastQ.gz\nHave Ctrl-C to quit, anything else to try again.\n";
			my $answer = <STDIN>;}
	elsif ((-e $outfastqdir) & (-d $outfastqdir)) {
		#then we load what's in to avoid useless decompression for duplicates
		my @tempdirarr;
		if (opendir (INDIR,"$outfastqdir")) { 
			@tempdirarr = grep {$_ ne "." & $_ ne ".."} readdir INDIR;#fetching all files/folders in folder
			closedir INDIR;
			if ($outfastqdir =~ m/([A-Z]\:)/i) {
				$outfastqdrive = $1;
				$outfastqdrivesize = (Win32::DriveInfo::DriveSpace("$outfastqdrive"))[5];;
				$outfastqdriveroom = (Win32::DriveInfo::DriveSpace("$outfastqdrive"))[6];
				$outfastqdriveroom = sprintf("%.0f", ($outfastqdriveroom/1000000));
				$outfastqdrivesize = sprintf("%.0f", ($outfastqdrivesize/1000000));
				#now loading only the fastq files already existing in this folder
				$existingfastqarr[0] = 0;
				my $kc1 = 0;
				while (defined($tempdirarr[$kc1])) {
					if ($tempdirarr[$kc1] =~ m/\.FASTQ$/i) {
						$existingfastqarr[++$existingfastqarr[0]] = $tempdirarr[$kc1];}
					++$kc1;}
				last OUTFASTQDIR;}
			else {
				print "Sorry cannot identify drive for output fastq folder $outfastqdir.\nHave Ctrl-C to quit, anything else to try again.\n";
				my $answer = <STDIN>;}}	
		else {
			print "Sorry cannot read from this fastq output folder $outfastqdir.\nHave Ctrl-C to quit, anything else to try again.\n";
			my $answer = <STDIN>;}}
	else {
		print "\nWarning: Cannot find proposed folder or is not a folder.\nHave Ctrl-C to quit, anything else to to try again.\n";
		my $answer = <STDIN>;}}
print "\nFastQ output folder identified ($outfastqdir) on drive $outfastqdrive (space = $outfastqdrivesize mb, room = $outfastqdriveroom mb) and containing already $existingfastqarr[0] fastq files\n";
################################################################################
#677968693279877869823280736982826932696779678132535748555051505432666947666976#
#output folder for archiving fastqgz files
my $outfastqgzdir;
my @existingfastqgzarr;
my $outfastqgzdrive;
my $outfastqgzdrivesize;
my $outfastqgzdriveroom;
OUTFASTQGZDIR:
while(1>0) {
	$outfastqgzdir = Win32::GUI::BrowseForFolder(
		-editbox => 1,
        -folderonly => 1,
        -title => "Select an existing output folder for archiving FastQ.gz",);
    if (not defined($outfastqgzdir)) {
		print "\nWarning: You need to select one existing output folder for archiving FastQ.gz\nHave Ctrl-C to quit, anything else to try again.\n";
			my $answer = <STDIN>;}
	elsif ((-e $outfastqgzdir) & (-d $outfastqgzdir)) {
		#then we load what's in to avoid useless decompression for duplicates
		my @tempdirarr;
		if (opendir (INDIR,"$outfastqgzdir")) { 
			@tempdirarr = grep {$_ ne "." & $_ ne ".."} readdir INDIR;#fetching all files/folders in folder
			closedir INDIR;
			if ($outfastqgzdir =~ m/([A-Z]\:)/i) {
				$outfastqgzdrive = $1;
				$outfastqgzdrivesize = (Win32::DriveInfo::DriveSpace("$outfastqgzdrive"))[5];;
				$outfastqgzdriveroom = (Win32::DriveInfo::DriveSpace("$outfastqgzdrive"))[6];
				$outfastqgzdriveroom = sprintf("%.0f", ($outfastqgzdriveroom/1000000));
				$outfastqgzdrivesize = sprintf("%.0f", ($outfastqgzdrivesize/1000000));
				#now loading only the fastq files already existing in this folder
				$existingfastqgzarr[0] = 0;
				my $kc1 = 0;
				while (defined($tempdirarr[$kc1])) {
					if ($tempdirarr[$kc1] =~ m/\.FASTQ$/i) {
						$existingfastqgzarr[++$existingfastqgzarr[0]] = $tempdirarr[$kc1];}
					++$kc1;}
				last OUTFASTQGZDIR;}
			else {
				print "Sorry cannot identify drive for archiving fastq.gz folder $outfastqgzdir.\nHave Ctrl-C to quit, anything else to try again.\n";
				my $answer = <STDIN>;}}	
		else {
			print "Sorry cannot read from this fastq.gz archinving folder $outfastqgzdir.\nHave Ctrl-C to quit, anything else to try again.\n";
			my $answer = <STDIN>;}}
	else {
		print "\nWarning: Cannot find proposed folder or is not a folder.\nHave Ctrl-C to quit, anything else to to try again.\n";
		my $answer = <STDIN>;}}
print "\nFastQ.gz archiving folder identified ($outfastqgzdir) on drive $outfastqgzdrive (space = $outfastqgzdrivesize mb, room = $outfastqgzdriveroom mb) and containing already $existingfastqgzarr[0] fastq.gz files\n";
################################################################################
#677968693279877869823280736982826932696779678132535748555051505432666947666976#
#evaluation of room left for decompressing and archiving
if (uc $outfastqgzdrive ne uc $outfastqdrive) {#not the same drive used for fastq annd archiving fastq.gz
	my $verifyfastq = $outfastqdriveroom - $fastqsize;#whats going to be left after
	if ((($outfastqdrivesize-$verifyfastq)/$outfastqdrivesize) >  $sizestopfactor ) {
		die "\nSorry there is not enough room on the drive $outfastqdrive selected for decompressing Fastq.gz files. You need to select another location with more room (decompressed fatsq.gz my require up to $fastqsize mb\n";}
	elsif ((($outfastqdrivesize-$verifyfastq)/$outfastqdrivesize) >  $sizewarningfactor ) {
		print "\nWarning: you are going to use almost the full room left on drive $outfastqdrive selected for decompressing Fastq.gz files.\n";}
	else {
		print "\nOK. Room left after decompression of Fastq.gz will be $verifyfastq mb\n";}
	my $verifyfastqgz = $outfastqgzdriveroom - $fastqgzsize;#whats going to be left after
	if ((($outfastqgzdrivesize-$verifyfastqgz)/$outfastqgzdrivesize) >  $sizestopfactor ) {
		die "\nSorry there is not enough room on the drive $outfastqgzdrive selected for archiving Fastq.gz files. You need to select another location with more room (archiving fatsq.gz my require up to $fastqgzsize mb\n";}
	elsif ((($outfastqgzdrivesize-$verifyfastqgz)/$outfastqgzdrivesize) >  $sizewarningfactor ) {
		print "\nWarning: you are going to use almost the full room left on drive $outfastqgzdrive selected for archiving Fastq.gz files.\n";}
	else {
		print "\nOK. Room left after archiving of Fastq.gz will be $verifyfastqgz mb\n";}}
else {
	my $verifyfastq = $outfastqdriveroom - $fastqsize - $fastqgzsize;#whats going to be left after
	if ((($outfastqdrivesize-$verifyfastq)/$outfastqdrivesize) >  $sizestopfactor ) {
		die "\nSorry there is not enough room on the drive $outfastqdrive selected for decompressing/archiving Fastq.gz files. You need to select another location with more room (decompressed + archived fatsq.gz my require up to $fastqsize + $fastqgzsize mb\n";}
	elsif ((($outfastqdrivesize-$verifyfastq)/$outfastqdrivesize) >  $sizewarningfactor ) {
		print "\nWarning: you are going to use almost the full room left on drive $outfastqdrive selected for decompressing/archiving Fastq.gz files.\n";}
	else {
		print "\nOK. Room left after decompression/archiving of Fastq.gz will be $verifyfastq mb\n";}}
################################################################################
#677968693279877869823280736982826932696779678132535748555051505432666947666976#
#so let's go for gunzip and archiving but only if not yet existing
my $showtime = getdate();
print "\n$showtime: Starting process.\n";
my $ku1 =1;
while ($ku1 <= $fastqgzfinalarr[0][0]) {
	my $ku2 = 1;
	my $foundgunzip = 0;
	FOUNDGUNZIP:
	while ($ku2 <=$existingfastqarr[0]) {
		if (uc $existingfastqarr[$ku2] eq $fastqgzfinalarr[$ku1][3]) {
			$foundgunzip = 1;
			last FOUNDGUNZIP;}
		++$ku2;}
	if ($foundgunzip == 0) {
		system "\"$sevenzipexe\" e \"$fastqgzfinalarr[$ku1][2]\" -o\"$outfastqdir\" -aoa";
		$showtime = getdate();
		print "$showtime: decompressed $fastqgzfinalarr[$ku1][1]\n";}
	else {
		$showtime = getdate();
		print "$showtime: $fastqgzfinalarr[$ku1][2] already decompressed\n";}
	$ku2 = 1;
	my $foundarchive = 0;
	FOUNDARCHIVE:
	while ($ku2 <=$existingfastqgzarr[0]) {
		if (uc $existingfastqgzarr[$ku2] eq $fastqgzfinalarr[$ku1][1]) {
			$foundarchive = 1;
			last FOUNDARCHIVE;}
		++$ku2;}
	if ($foundarchive == 0) {
		my $tocopy = $fastqgzfinalarr[$ku1][2];
		my $copy = 	$outfastqgzdir . "\\$fastqgzfinalarr[$ku1][1]";
		copy($tocopy, $copy);
		$showtime = getdate();
		print "$showtime: $fastqgzfinalarr[$ku1][1] archived\n";}
	else {
		$showtime = getdate();
		print "$showtime: $fastqgzfinalarr[$ku1][1] already archived\n";}
	++$ku1;}
 $showtime = getdate();
print "\n$rundatetime / $showtime: Process completed\n";
die;
#function used in File::Find fior recursive
sub recordentry {
	my $infile = $_;
	while($infile =~ s/\//\\/){}
    my $fullname = $File::Find::name;
    #next is to be sure everything is windows compatible with backslaches
    while ($fullname =~ s/\//\\/) {}
    if($infile =~ m/\.FASTQ\.GZ$/i) {
		$fastqgzarr[++$fastqgzarr[0][0]][1] = $infile;
		$fastqgzarr[$fastqgzarr[0][0]][2] = $fullname;
		if ($infile =~ m/^(.+)\.GZ$/i) {#corresponding name gunzip
			$fastqgzarr[$fastqgzarr[0][0]][3] = $1;}}
	return();}
#function to search for 7zip executable	
sub search7zip {
	my $infile = $_;
	my $fullname = $File::Find::name;
	while ($fullname =~ s/\//\\/) {}
	if($infile=~ m/^7z\.exe$/i) {
		$sevenzipexe = $fullname;}
	return();}
###########OK
sub getdate {
    #sub to capture GMT date and time
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime;#catch date starting process
    $year = $year + 1900;#$year was number of years since 1900
    $mon = $mon + 1;#first day is zero!!!
    if ($mon < 10) {
	$mon = "0" . $mon;}
    if ($mday < 10) {
	$mday = "0" . $mday;}
    if ($hour < 10) {
	$hour = "0" . $hour;}
    if ($min < 10) {
	$min = "0" . $min;}
    if ($sec < 10) {
	$sec = "0" . $sec;}
    my $date = "$year$mon$mday";
    my $time = "$hour$min$sec";
    my $xdatetime = $date ."_$time";
    return $xdatetime;}
#677968693279877869823280736982826932696779678132535748555051505432666947666976#
################################################################################

=head1 NAME

FastQmanager_v1i1.pl

=head1 HELP

This code has been developped on a Windows 10 64bit platform.

This codes requires Perl installed as well as some Perl modules as well
as 7-Zip executable for Windows 64bit platforms.

Scope:

This code is designed to catch all FastqQ.gz files recursively from one or more 
locations (Drive:Folder), decompressing these in a defined existing location and 
archiving these FastQ.gz in a specific existing location.

You will be prompted for all of these (starting with the location of the 7-Zip
executable) along (at the begining of) the process.

Important: The output folders MUST exist prior running this code.

For additionnal info. contact the code Owner/Author.

=head1 VERSION

This documentation refers to FastQmanager_v1i1.pl (version 1, issue 1)

=head1  USAGE

	perl FastQmanager_v1i1.pl

=head1 REQUIRED ARGUMENTS

=over

=back

=head1 OPTIONS

=over

=back
	
=item --version

=item --usage

=item --help

=item --man

=back

=head1 AUTHOR

LECOCQ Pierre 59072327136 (Pierre.Lecocq@NGYX.eu)

=head1 BUGS

If bug is identified please contact code Owner/Author.

=head1 COPYRIGHT

Copyright (c) 2016APR06, code Owner: LECOCQ Pierre 59072327136 BE/BEL.
All Rights Reserved.

This program is not free software. It may not be used , redistributed
and/or modified without code Owner/Author authorisation. Exception to 
the previous is made for active members of the "Service de Génétique du 
Centre Hospitalier Universitaire de l'Université de Liège BE/BEL".

677968693279877869823280736982826932696779678132535748555051505432666947666976
