#!/bin/env perl

use strict;
use warnings;
use FindBin;
use File::Spec;

## usage:
##     perl terp_predicat.pl query.faa > prediction.tsv

print join("\t", 'Query_ID', 'prediCAT_Call', 'Forced_prediCAT_Call', 'prediCAT_NN_Dist', 'prediCAT_NN_Score', 'prediCAT_SNN_Score', 'prediCAT_Top_Hit_Description', 'prediCAT_Top_Hit_Organism', 'prediCAT_Top_Hit_Topology', 'prediCAT_Top_Hit_Accession', 'PID', 'Diamond nearest neighbor hit', 'Nearest_Neighbor_Hit')."\n";

my $qfile = shift;
my %qfa = %{fasta2hash($qfile)};

## Get annotations list
my %ann = ();
open my $afh, '<', File::Spec->catfile($FindBin::Bin, 'terpene_synthase_annotations.txt') or die $!;
while(<$afh>){
    next if($_ =~ m/^Description/);
    chomp;
    my ($desc, $mtype, $fullname, $org, $top) = split(/\t/, $_);
    $top =~ s/\s+/-/g;
    $top =~ s/-$//;
    my ($name, @rest) = split(/\s+/, $fullname);
    my $uname = $name;
    $name =~ s/_/-/g;
    $ann{$name} = {
	'desc' => $desc,
	'org' => $org,
	'top' => $top,
	'uname' => $uname
    };
}
close $afh;
$ann{'no_confident_result'} = {
    'desc' => 'NA',
    'org' => 'NA',
    'top' => 'NA',
    'uname' => 'NA'
};

my $wildcard = 'UNK';
my @dir = split(/\//, $qfile);
my $trainfaa = File::Spec->catfile($FindBin::Bin, 'terp.faa');
my $db = File::Spec->catfile($FindBin::Bin, 'terp.db.dmnd');

foreach my $q (sort keys %qfa){
    open my $tf, '>', 'tmpt.fa' or die $!;
    print $tf '>'.$q.'_'."$wildcard\n".$qfa{$q}."\n";
    close $tf;
    ## Get PID
    system("diamond blastp -q tmpt.fa -d $db -o tmpt.dbp --quiet --evalue 0.01 > /dev/null") == 0 or die("diamond failed");
    my $id = 0;
    my $diamond_sid = '-';
    open my $ffh, '<', 'tmpt.dbp' or die $!;
    while(<$ffh>){
	chomp;
	my ($qid, $sid, $pid, @rest) = split(/\t/, $_);
	if($id < $pid){
	    $diamond_sid = $sid;
	    $diamond_sid =~ s/ref[ab]-//g;
	    $id = $pid;
	}
    }
    close $ffh;
    ## PrediCAT
    my ($at, $atname, $atf, $nndist, $nnscore, $snnscore, $nearest) = treescanner($trainfaa, 'tmpt.fa', $wildcard);
    $at = 'NA' unless(defined $at && $at=~m/\S/);
    $atname = 'NA' unless(defined $atname && $atname=~m/\S/);
    $atname =~ s/ref[ab]-//g;
    $atf = 'NA' unless(defined $atf && $atf=~m/\S/);
    $nndist = 'NA' unless(defined $nndist && $nndist=~m/\S/);
    $nnscore = 'NA' unless(defined $nnscore && $nnscore=~m/\S/);
    $snnscore = 'NA' unless(defined $snnscore && $snnscore=~m/\S/);
    $nearest = 'NA' unless(defined $nearest && $nearest=~m/\S/);
    my $hit_desc = $ann{$atname}{'desc'};
    $hit_desc = 'NA' if (!defined $hit_desc);
    my $hit_org = $ann{$atname}{'org'};
    $hit_org = 'NA' if (!defined $hit_org);
    my $hit_top = $ann{$atname}{'top'};
    $hit_top = 'NA' if (!defined $hit_top);
    my $hit_uname = $ann{$atname}{'uname'};
    $hit_uname = 'NA' if (!defined $hit_uname);
    print join("\t", $q, $at, $atf, $nndist, $nnscore, $snnscore, $hit_desc, $hit_org, $hit_top, $hit_uname, $id, $diamond_sid, $nearest) . "\n";
    #system("rm tmp*");
}

sub treescanner{
    my ($basefaa, $q, $wc) = @_;
    my $py = File::Spec->catfile($FindBin::Bin, 'treeparserscore.py');
    ## Grab first seq
    open my $bfh, '<', $basefaa or die $!;
    my ($tid, $tseq) = (undef, undef);
    while(<$bfh>){
    if($_ =~ m/^>(.+)/){
        $tid = $1;
    }else{
        $tseq = $_;
        last;
    }
    }
    close $bfh;
    open my $ttf, '>', 'tmp.trim.faa' or die $!;
    print $ttf '>'."$tid\n$tseq\n";
    close $ttf;
    system("cat $q >> tmp.trim.faa");
    ## Align query to first seq
    system("mafft --quiet --namelength 100 --op 5 tmp.trim.faa > tmp.trim.afa");
    ## Trim query
    open my $pfh, '<', 'tmp.trim.afa' or die $!;
    my ($head, $tail) = (undef, undef);
    my ($id, $s, $qcur, $qseq, $qlast) = (undef, undef, undef, '', undef);
    while(<$pfh>){
	chomp;
	if($_ =~ m/^>(.+)/){
	    $qlast = $qcur;
	    $qcur = $1;
	    next if($qseq eq '');
	    if($qlast =~ m/$wc/){
		($id, $s) = ($qlast, $qseq);
	    }
	    $qseq = '';
	}else{
	    $qseq .= $_;
	}
    }
    close $pfh;
    if($qcur =~ m/$wc/){
	($id, $s) = ($qcur, $qseq);
    }
    ($head, $tail) = ($qseq, $qseq);
    if($head =~ m/^-/){
	$head =~ s/^(-+).+/$1/;
    }else{
	$head = '';
    }
    if($tail =~ m/-$/){
	$tail =~ s/\w(-+)$/$1/;
    }else{
	$tail = '';
    }
    $head = length($head);
    $tail = length($tail);
    substr($s, -1, $tail) = '';
    substr($s, 0, $head) = '';
    $s =~ s/-//g;
    ## Compile all seqs
    open my $newfaa, '>', 'tmp.tree.faa' or die $!;
    print $newfaa '>' . $id . "\n" . $s . "\n";
    close $newfaa;
    system("cat $basefaa >> tmp.tree.faa") == 0 or die("creating tree fasta failed");
    system("perl -pi -e 's/[\(\)]/-/g' tmp.tree.faa") == 0 or die("replacing braces failed");
    ## Align all seqs, make tree
    #system("mafft --clustalout --quiet --namelength 60 tmp.tree.faa > tmp.tree.aln");
    system("mafft --quiet --namelength 100 tmp.tree.faa > tmp.tree.aln") == 0 or die("running mafft failed");
    #system("clustalw -TREE -INFILE=tmp.tree.aln -QUIET 1> /dev/null");
    system("fasttree -quiet < tmp.tree.aln > tmp.tree.ph 2>/dev/null") == 0 or die("running fasttree failed");
    ## Make calls
    chomp(my $cwd = `pwd -P`);
    system("python $py $cwd/tmp.tree.ph > tmp.tp.tsv") == 0 or die("running treparserscore failed");
    ## Parse calls
    open my $tp, '<', 'tmp.tp.tsv' or die $!;
    while(<$tp>){
	chomp;
	my ($i, $c, $atn, $fc, $nn, $nnsc, $snnsc) = split(/\t/, $_);
	if(defined $c && defined $fc){
	    close $tp;
	    return ($c, $atn, $fc, $nn, $nnsc, $snnsc, $i);
	}
    }
    close $tp;	
}

sub fasta2hash{
    my %s = ();
    my @id = ();
    my @seq = ();
    open my $qfh, '<', shift or die $!;
    while(<$qfh>){
	chomp;
	if($_ =~ m/^>([\d\w]+)/){
	    push @id, $1;
	}else{
	    my $lastid = scalar(@id)-1;
	    if(exists $seq[$lastid]){
		$seq[$lastid] .= $_;
	    }else{
		push @seq, $_;
	    }
	}
    }
    for(my $i=0;$i<scalar(@id);$i++){
	$s{$id[$i]} = $seq[$i];
    }
    return(\%s);
}
