# vim: set fileencoding=utf-8 :
#
# Copyright (C) 2016 Marnix H. Medema
# Wageningen University, Bioinformatics Group
#
# Copyright (C) 2016 Marc Chevrette
# University of Wisconsin, Madison; Currie Lab
#
# License: GNU Affero General Public License v3 or later
# A copy of GNU AGPL v3 should have been included in this software package in LICENSE.txt.
'''
Terpene cyclization pattern prediction based on terpene synthase amino acid
sequences.
'''

import logging
from Bio.SeqFeature import SeqFeature, FeatureLocation
from antismash import utils
from helperlibs.wrappers.io import TemporaryDirectory
import os
import sys


def specific_analysis(seq_record, options):
    utils.log_status("Calulating detailed predictions for Terpene clusters.")
    #Find terpene clusters
    terpene_clusters = utils.get_cluster_features_of_type(seq_record, "terpene")
    #For each, find canonical terpene synthases
    terpene_synthases = find_terpene_synthases(terpene_clusters, seq_record)
    #For each TS, run predictor
    outputs = run_terp_predicat(terpene_synthases)
    #Parse and store output in seq_record object
    store_terp_predicat_results(terpene_synthases, outputs)


def find_terpene_synthases(clusters, seq_record):
    """Find terpene synthase-encoding CDS features"""
    terpene_synthases = []
    for cluster in clusters:
        for feature in utils.get_cluster_cds_features(cluster, seq_record):
            if 'sec_met' in feature.qualifiers:
                for annotation in feature.qualifiers['sec_met']:
                    if "Domains detected:" in annotation and "Terpene" in annotation:
                        terpene_synthases.append(feature)
                        break
    return terpene_synthases

def run_terp_predicat(terpene_synthases):
    """Run Terpene PrediCAT for each terpene synthase"""
    outputs = {}
    terp_predicat_dir = os.path.dirname(os.path.abspath(__file__))
    with TemporaryDirectory(change=True):
        for terpene_synthase in terpene_synthases:
            #Write CDS feature to FASTA file
            terp_seqs_file = "input_tps.fasta"
            terp_seqs = [str(utils.get_aa_sequence(terpene_synthase))]
            terp_names = [utils.get_gene_id(terpene_synthase)]
            utils.writefasta(terp_names, terp_seqs, terp_seqs_file)
            #Run PrediCAT
            predicat_command = [terp_predicat_dir + os.sep + 'predictterp_nodep.sh', 'input_tps.fasta', terp_predicat_dir]
            out, err, retcode = utils.execute(predicat_command)
            if retcode != 0:
                logging.error("Failed to run PrediCAT for terpenes: %r", err)
                raise RuntimeError("terpene PrediCat failed: %s" % err)
            outputs[utils.get_gene_id(terpene_synthase)] = out.partition("\n")[2]
            os.remove(terp_seqs_file)
    return outputs


def store_terp_predicat_results(terpene_synthases, outputs):
    """Parses output and stores it in the terpene CDS features"""
    terp_annotationdict = {}
    for line in open(utils.get_full_path(__file__, "terpene_synthase_annotations_vs_accessions.txt"), "r"):
        line = line.rstrip('\n')
        val, key = line.split('\t')[:2]
        terp_annotationdict[key] = val

    for terpene_synthase in terpene_synthases:
        if 'note' in terpene_synthase.qualifiers:
            annotations = terpene_synthase.qualifiers['note']
        else:
            annotations = []
            terpene_synthase.qualifiers['note'] = annotations

        output = outputs[utils.get_gene_id(terpene_synthase)]
        pred = TerpenePredicatPrediction(output)
        besthit_acc = pred.diamond_nearest_neighbour_hit.partition("_")[0].replace("-", "_")
        besthit_name = terp_annotationdict.get(besthit_acc, "No BLAST hit")

        if pred.cyclization_pattern == "no_confident_result":
            annotations.append("Cyclization pattern: no prediction")
            annotations.append("Nearest neighbour: none found")
            if besthit_name == "No BLAST hit":
                annotations.append("Best known BLAST hit: %s" % (besthit_name))
            else:
                annotations.append("Best known BLAST hit: %s (%s, %s %%ID)" % (besthit_name, besthit_acc, pred.percent_identity))
        else:
            cycl_pattern = pred.cyclization_pattern
            nn = pred.nearest_neighbour
            pid = pred.percent_identity

            annotations.append("Cyclization pattern: %s" % cycl_pattern)
            if pred.top_hit_accession != "NA":
                logging.debug("top hit accession: %s", pred.top_hit_accession)
                annotations.append("Nearest neighbour: %s (%s)" % (terp_annotationdict.get(pred.top_hit_accession), nn))
            else:
                annotations.append("Nearest neighbour: none found")
            if besthit_name == "No BLAST hit":
                annotations.append("Best known BLAST hit: %s" % (besthit_name))
            else:
                annotations.append("Best known BLAST hit: %s (%s, %s %%ID)" % (besthit_name, besthit_acc, pid))


# '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'

class TerpenePredicatPrediction(object):
    def __init__(self, predicat_line):
        parts = predicat_line.replace("\n", "").split("\t")
        self.query_id = parts[0]
        self.cyclization_pattern = parts[1]
        self.forced_call = parts[2]
        self.nn_dist = parts[3]
        self.nn_score = parts[4]
        self.snn_score = parts[5]
        self.top_hit_desc = parts[6]
        self.top_hit_organism = parts[7]
        self.top_hit_topology = parts[8]
        self.top_hit_accession = parts[9]
        self.percent_identity = parts[10]
        self.diamond_nearest_neighbour_hit = parts[11]
        self.nearest_neighbour_hit = parts[12]

        self.nearest_neighbour = "{}, {}".format(self.top_hit_accession, self.top_hit_organism)

