#!/usr/bin/env perl

use strict;
use warnings;
use Data::Dumper;
use POSIX qw(strftime);
use Getopt::Long qw(HelpMessage GetOptionsFromArray);
use Pod::Usage;
use Cwd qw(getcwd realpath);
use YAML::Tiny;
use FindBin qw($Bin);
use lib "$Bin/../lib";

use MiRNAture::Evaluate; #Evaluation CMsearch
use MiRNAture::ConfigFile; #Analyse configuration file class
use MiRNAture::Blast;
use MiRNAture::_BlastSearch;
use MiRNAture::BlastPrepareQueries;
use MiRNAture::HMMsearch; #HMM subroutine library
use MiRNAture::ToolBox;
use MiRNAture::HMM;
use MiRNAture::CM;
use MiRNAture::Others;
use MiRNAture::FinalCandidates;
use MiRNAture::LogFile;

### Input data
my $nameC = ""; #cmlist file
my @path_cm = ""; #cm_path
my @path_hmm = ""; #hmm_path
my $mode = ""; #run mode
my $species = ""; #target species genomes
my $strategy = ""; #Blast strategy
my $blastQueriesFolder = ""; # Path blast queries
my $work_folder = ""; #output folder
my $name_species = ""; #Scientific name of species
my $data_folder = "";
my $parallel_run = "";
my $parallel_linux = "";
my $rep_cutoff; # Homology cutoff to speed-up the searches 
my $maxthresholdBit; # nBitscore threshold to clean data: 0-1

my $help = 0; 
my $man = 0;
my @cm_model_others = ""; #Possible path to new CMs != from RFAM ones
my @original_ARGV = @ARGV;
my @strategy;
GetOptions (
    'cmlist|l=s' => \$nameC,
    'cmpath|cmp=s{1,3}' => \@path_cm, # including CMs from multiple sources
    'hmmpath|hmmp=s{1,3}' => \@path_hmm, # including HMMs from multiple sources 
    'mode|m=s' => \$mode,
    'species|spe=s' => \$species,
    'species_name|n_spe=s' => \$name_species,
    'workdir|w=s' => \$work_folder,
    'repetition_cutoff|rep=s' => \$rep_cutoff,
    'nbitscore_cutoff|nb_cut=f' => \$maxthresholdBit, #CAVH
    'help|h' => \$help,
    man => \$man,
    'strategy|str=s' => \@strategy,
    'blast_queries|blstq=s' => \$blastQueriesFolder,
    'data_folder|data=s' => \$data_folder,
    'parallel|ps=i' => \$parallel_run, # Parallel SLURM
    'parallel_linux|pe=i' => \$parallel_linux, # Parallel LINUX
    'new_models|nmodels=s{1,2}' => \@cm_model_others,
) or pod2usage(2);
@strategy = split (/,/, join(',',@strategy));

my $startComplete = time();
#### Flags Evaluation
my $new_model_path = $cm_model_others[-1];
$rep_cutoff = evaluate_input_flags($nameC, $mode, $work_folder, $species, $parallel_run, $parallel_linux, $rep_cutoff, $new_model_path);
my $configuration_mirnature = read_config_file("$work_folder/../miRNAture_configuration_$species.yaml");
my $current_dir = $configuration_mirnature->[3]->{Default_folders}->{Output_folder}."/TemporalFiles"; #getcwd;
## Working Paths
get_basic_files($configuration_mirnature->[3]->{Default_folders}->{Data_folder});

#Time variables
my ($start_hmm, $start_other, $start_infernal, $start_blast, $start_user);

my $name = $nameC;
$name =~ s/(.*\/Data\/|\.\/Data\/|\/.*\/|\.\/)(.*)/$2/g;
my $tag = ((strftime "%H%M%S%d%m%Y", localtime) + (int(rand(10)))); #Today date + random number 0..10.
print_process("Processing: $name models on $name_species genome");

my $input_line = join " ", @original_ARGV; 

##Create Configuration and Log File
my $configuration_file = MiRNAture::ConfigFile->new(
    tag => $tag, 
    list_file => $nameC,
    data_folder => $data_folder,
);
$configuration_file->include_running_mode($mode);
my ($temp_cm, $temp_hmm) = $configuration_file->subset_search_models(\@path_cm, \@path_hmm, \@cm_model_others);

my $tag_spe = $configuration_mirnature->[3]->{"Species_data"}->{"Tag"};
my $genome_path = $configuration_mirnature->[3]->{"Species_data"}->{"Genome"};
my $genomes = $configuration_file->read_genomes_paths($tag_spe, $genome_path);

my $log_file = MiRNAture::LogFile->new(
    log_file_input => "$work_folder/LOGs/miRNAture_homology_log_${species}_${mode}_$tag.log",
    command_line => $input_line,		
);
$log_file->create_log_folder($work_folder);
$log_file->create_file;

#Key Output locations: Hard-Coded
my @species;
my $outHMM = "$work_folder/HMMs";
my $outBlast = "$work_folder/Blast";
my $outInfernal = "$work_folder/Rfam";
my $outOther = "$work_folder/Mirbase";
my $outUser = "$work_folder/User_models";
my $out_final_path = "$work_folder";

##My Result files:
my ($blast_output, $hmm_output, $infernal_output, $mirbase_output, $user_output);

##Load basic databases
my $basicFiles = "$data_folder/Basic_files";
my ($bitscores, $len_r, $names_r, $names_r_inverse, $families_names);
my $user_data_path = $configuration_mirnature->[3]->{Default_folders}{"User_folder"};

# Infer user cm scores
if (length $user_data_path > 0){ #If path defined check that exists scores file
       	if (!-e "$user_data_path/all_user_scores.txt" || -z "$user_data_path/all_user_scores.txt"){ #Don't exist, infer
	       	print_process("Inferring scores from your miRNA CMs");
	       	infer_data_from_cm("$user_data_path/CMs", $user_data_path, "all_user_scores.txt"); #Here miRNAture generates the score's file: all_Other_scores.txt in the new CM folder.
      	}
}

if ($mode =~ m/mirbase/){ #Load specific scores for mirbase.
	    ($bitscores, $len_r, $names_r, $names_r_inverse, $families_names) = load_all_databases("mirbase", $basicFiles, $user_data_path); 
    #}
} elsif ($mode =~ m/rfam/) { #Here get all the scores from RFAM database
    ($bitscores, $len_r, $names_r, $names_r_inverse, $families_names) = load_all_databases("Basic", $basicFiles, $user_data_path); 
} elsif ($mode =~ m/user/) { #Here get all the scores from RFAM database
    ($bitscores, $len_r, $names_r, $names_r_inverse, $families_names) = load_all_databases("user", $basicFiles, $user_data_path); 
} else { #Here get all the scores from RFAM and miRBase for: blast, hmm and final
	if (length $user_data_path > 0){ #If path defined check that exists scores file
		($bitscores, $len_r, $names_r, $names_r_inverse, $families_names) = load_all_databases("Joined", $basicFiles, $user_data_path); 
	} else {
		($bitscores, $len_r, $names_r, $names_r_inverse, $families_names) = load_all_databases("JoinedN", $basicFiles, $user_data_path); 
	}
}

#Check that genome tag has genome path
if (!exists $$genomes{$species}){
    print_error("Your sequence tag is not correct respect to genomes file");
}
my $genome_path_complete = $$genomes{$species};
my ($Zvalue, $genome_size_bp) = calculate_Z_value($genome_path_complete, "Genome");
my $minBitscore = calculate_minimum_bitscore($genome_size_bp);

# Modes
if ($configuration_file->mode eq "blast"){
    print_process("Running Mode: ".$configuration_file->mode." searches");	
    write_line_log($log_file, "# Running Mode: ".$configuration_file->mode." at ".localtime."\n");
    $start_blast = time;
    my $speciesTag = detect_blast_queries($blastQueriesFolder);
    index_query_genome($$genomes{$species}, $configuration_mirnature->[2]->{Program_locations}->{makeblastdb});
    create_folders("$work_folder", "Blast");
    for (my $i = 0; $i<=$#strategy; $i++){
        print_process("Running on strategy $strategy[$i]");
        my $start_blast_str = time;
        my $blast_experiment = MiRNAture::_BlastSearch->new(
            blast_str => $strategy[$i],
            output_folder => "$work_folder/Blast",
            query_folder => $blastQueriesFolder,
            genome_subject => $configuration_mirnature->[3]->{Species_data}->{Genome},
            subject_species => $species,
            data_folder => $data_folder,
            current_directory => $current_dir,
            path_covariance => \@$temp_cm,
            bitscores_CM => $bitscores,
            length_CM => $len_r,
            names_CM => $names_r,
            parallel_running => $parallel_linux,
            blast_program_path => $configuration_mirnature->[2]->{Program_locations}->{blastn},
            cmsearch_program_path => $configuration_mirnature->[2]->{Program_locations}->{cmsearch},
            makeblast_program_path => $configuration_mirnature->[2]->{Program_locations}->{makeblastdb},
            models_list => $configuration_mirnature->[3]->{Default_folders}->{List_cm_miRNAs},
            user_data => $user_data_path,
        );	
        if ($blast_experiment->blast_str =~ /^\d+$/){
            my ($molecules, $query_species, $families, $files_relation) = $blast_experiment->searchHomologySequenceBlast_parallel($speciesTag); #Run all blastn jobs, returns array by Str
            $blast_experiment->searchHomologyBlast($molecules, $query_species, $families, $files_relation, $Zvalue, $minBitscore,$maxthresholdBit);	
        } elsif ($blast_experiment->blast_str =~ /^ALL$/){
            $blast_experiment->join_all_blast_str;
            $blast_experiment->clean_empty;
        }
        my $diff = time - $start_blast_str;
        write_line_log($log_file, "# Running time for strategy ".$blast_experiment->blast_str." : ".$diff." s\n");
    }
    my $diff = time - $start_blast;
    write_line_log($log_file, "# Total time for ".$configuration_file->mode." : ".$diff." s\n");
} elsif ($configuration_file->mode eq "hmm"){
    print_process("Running ".$configuration_file->mode." searches mode");	
    write_line_log($log_file, "# Running Mode: ".$configuration_file->mode." at ".localtime."\n");
    $start_hmm = time;
    my $hmm_experiment = MiRNAture::HMM->new(
        parallel_linux => $parallel_linux,
        genome_subject => $$genomes{$species},
        subject_species => $species,
        output_folder => $outHMM,
        path_covariance => \@$temp_cm,
        path_hmm_models => \@$temp_hmm,
        bitscores_CM => $bitscores,
        length_CM => $len_r,
        names_CM => $names_r,
        families_names_CM => $families_names,
        nhmmer_program_path => $configuration_mirnature->[2]->{Program_locations}->{nhmmer},
        cmsearch_program_path => $configuration_mirnature->[2]->{Program_locations}->{cmsearch},
        list_models => $configuration_file->list_file->stringify, 
    );
    $hmm_experiment->create_folders_hmm($work_folder);
    $hmm_experiment->search_homology_HMM($Zvalue, $minBitscore, $maxthresholdBit);						
    $hmm_experiment->clean_empty;
} elsif ($configuration_file->mode eq "rfam"){
    print_process("Running ".$configuration_file->mode." searches mode");	
    write_line_log($log_file, "# Running Mode: ".$configuration_file->mode." at ".localtime."\n");
    $start_infernal = time;
    my $cm_experiment = MiRNAture::CM->new(
        genome_subject => $$genomes{$species},
        subject_species => $species,
        output_folder => $outInfernal,
        path_covariance => \@$temp_cm,
        bitscores_CM => $bitscores,
        length_CM => $len_r,
        names_CM => $names_r,
        families_names_CM => $families_names,
        cmsearch_program_path => $configuration_mirnature->[2]->{Program_locations}->{cmsearch},
    );
    $cm_experiment->create_folders_cm;
    $cm_experiment->search_homology_CM($Zvalue,$minBitscore,$maxthresholdBit);
    $cm_experiment->clean_empty;
} elsif ($configuration_file->mode eq "mirbase"){
    print_process("Running Mode: ".$configuration_file->mode." searches");	
    write_line_log($log_file, "# Running Mode: ".$configuration_file->mode." at ".localtime."\n");
    $start_other = time;
    my $other_experiment = MiRNAture::Others->new(
        genome_subject => $$genomes{$species},
        subject_species => $species,
        output_folder => $outOther,
        path_covariance => \@$temp_cm, 
        bitscores_CM => $bitscores, 
        length_CM => $len_r,
        names_CM => $names_r,
        families_names_CM => $families_names,
        cmsearch_program_path => $configuration_mirnature->[2]->{Program_locations}->{cmsearch},
    );					
    $other_experiment->create_folders_other;
    $other_experiment->search_homology_other($Zvalue,$minBitscore,$maxthresholdBit);
    $other_experiment->clean_empty;
} elsif ($configuration_file->mode eq "user"){
    print_process("Running Mode: ".$configuration_file->mode." searches");	
    write_line_log($log_file, "# Running Mode: ".$configuration_file->mode." at ".localtime."\n");
    $start_user = time;
    my $other_experiment = MiRNAture::Others->new(
        genome_subject => $$genomes{$species},
        subject_species => $species,
        output_folder => $outUser,
        path_covariance => \@$temp_cm, 
        bitscores_CM => $bitscores, 
        length_CM => $len_r,
        names_CM => $names_r,
        families_names_CM => $families_names,
        cmsearch_program_path => $configuration_mirnature->[2]->{Program_locations}->{cmsearch},
    );					
    $other_experiment->create_folders_other;
    $other_experiment->search_homology_other($Zvalue,$minBitscore,$maxthresholdBit);
    $other_experiment->clean_empty;
} elsif ($configuration_file->mode eq "final"){
    ;
} else {
    print_error("Not recognized homology mode: ".$configuration_file->mode.".")
}

print_process("Merging candidates on ".$configuration_file->mode);	
## Merging candidates by Strategy
if ($configuration_file->mode eq "hmm"){
    define_final_CMs("$outHMM/$species/Infernal/Final", $species, $configuration_file->mode, $len_r);
    my $diff = time - $start_hmm;
    write_line_log($log_file, "# Total running time: ".$diff." s\n");
} elsif ($configuration_file->mode eq "rfam"){
    define_final_CMs("$outInfernal/$species/Final", $species, $configuration_file->mode, $len_r);
    my $diff = time - $start_infernal;
    write_line_log($log_file, "# Total running time: ".$diff." s\n");
} elsif ($configuration_file->mode eq "mirbase"){
    define_final_CMs("$outOther/$species/Final", $species, $configuration_file->mode, $len_r);
    my $diff = time - $start_other;
    write_line_log($log_file, "# Total running time: ".$diff." s\n");
} elsif ($configuration_file->mode eq "user"){
    define_final_CMs("$outUser/$species/Final", $species, $configuration_file->mode, $len_r);
    my $diff = time - $start_user;
    write_line_log($log_file, "# Total running time: ".$diff." s\n");
}

# Final Merge
if ($configuration_file->mode eq "final"){
    print_process("Refining final candidates on ".$species);	
    ####Preliminar Results ###
    $blast_output = "$outBlast/$species/Infernal/Final/all_RFAM_${species}_ALL.truetable.joined.table"; #Blast
    $hmm_output = "$outHMM/$species/Infernal/Final/all_RFAM_${species}.truetable.clean.joined.table"; #HMMs
    $infernal_output = "$outInfernal/$species/Final/all_RFAM_${species}.truetable.joined.table"; #Direct Infernal
    $mirbase_output = "$outOther/$species/Final/all_RFAM_${species}.truetable.joined.table"; #Other direct CMs
    $user_output = "$outUser/$species/Final/all_RFAM_${species}.truetable.joined.table"; #Other direct CMs
    my $final_candidates = MiRNAture::FinalCandidates->new(
        blast_results => $blast_output,
        hmm_results => $hmm_output,
        infernal_results => $infernal_output,
        other_results => $mirbase_output,
        user_results => $user_output,
        output_folder => $work_folder,	
        subject_species => $species,
        genome_subject => $configuration_mirnature->[3]->{Species_data}{"Old_Genome"}, #$genomes{$species}, # Here the original one
        names_CM => $names_r_inverse,
        length_CM => $len_r,
        families_names_CM => $families_names,
        species_name => $name_species,
        species_genome_new_database => $configuration_mirnature->[3]->{Species_data}{Database_names_genome},
        repetition_rules => $rep_cutoff,
    );
    $final_candidates->create_folders_final;
    $final_candidates->generate_final_output;
    $final_candidates->get_fasta_sequences;
    $final_candidates->get_small_genomes;
    $final_candidates->generate_gff_homology;
    my $end = time(); 
    my $diff = $end - $startComplete;
    write_line_log($log_file, "# Total running time: ".$diff." s\n");
}

__END__

=head1 NAME
C<miRNAture> - Computational homology searches on a genomic DNA

=head1 SYNOPSIS

./run_homology.pl [-options]

Options:

-help	 brief help message

-man 	 full documentation

Author:

I<Cristian A. Velandia Huerto>

=head1 OPTIONS

=over 12

=item -help           

print this documentation

=item -man 

Prints the manual page and exits.

=item -cmlist            

list of CM to evaluate over target genome

=item -mode 

running mode: BLAST, HMM, INFERNAL, ALL, Final

=item -species

target species, write tag referenced on genomes file

=item -workdir 

current working directory

=item -strategy 

if selected BLAST as -mode write the strategy number  

=back 

=head1 DESCRIPTION

B<miRNAture> scan sequence fasta file for homologus metazoan microRNAs

=head1 AUTHOR

I<Cristian A. Velandia Huerto>

=head1 BUGS, CAVEATS, COMPLAINS or DONATIONS 
Write directly to cristian at bioinf.uni-leipzig.de

=cut 
