#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Definition of conservation related sequence alphabets
"""

import math
import bisect

__author__ = "Bulak Arpat"
__copyright__ = "Copyright 2019, Bulak Arpat"
__license__ = "GPLv3"
__version__ = "0.0.1"
__maintainer__ = "Bulak Arpat"
__email__ = "Bulak.Arpat@unil.ch"
__status__ = "Development"


# These constants were drived from dsitribution of PhyloP
# scores across mouse CDSs. Min and max corresponds roughly
# to 0.1 and 0.9 quantiles.

PHYLOP_MIN = -0.25
PHYLOP_MAX = 6.5
PHYLOP_STEP = (PHYLOP_MAX - PHYLOP_MIN) / 91

    

SIMPLE_CODON = {
    'Hco': 'HighlyConserved',
    'Con': 'Conserved',
    'Neu': 'Neutral',
    'Acc': 'Accelerated',
    'Hac': 'HighlyAccelerated', 
    'Unk': 'Unknown' 
}

SIMPLE_CODON_MIN = [-20, -5, -3, 3, 5]
#SIMPLE_CODON_MIN = [-2, 2, 3, 4.3, 5]
SIMPLE_CODON_WORDS = ['Hac', 'Acc', 'Neu', 'Con', 'Hco']

SIMPLE_NUC = {
    '': '',
}


def phylop_2_chr(score):
    """Converts the phyloP score to ASCII to be used in
    a FASTA-like output format - single letter per position
    """
    if math.isnan(score):
        ascii = 33
    elif score < PHYLOP_MIN:
        ascii = 34
    elif score > PHYLOP_MAX:
        ascii = 126
    else:
        ascii = int(35 + (score - PHYLOP_MIN) / PHYLOP_STEP)
    return chr(ascii)


def chr_2_pyhlop(chr_score):
    ascii = ord(chr_score)
    if ascii == 33:
        score = float('nan')
    elif ascii == 34:
        score = mean([-20, PYHLOP_MIN])
    elif ascii < 126:
        score = (ascii - 35) * PHYLOP_STEP + PHYLOP_MIN + PHYLOP_STEP / 2
    else:
        score = mean([7.528, PYHLOP_MAX])
    return score

def phylop_codon(scores):
    """Converts 3 phyloP scores into a SIMPLE_CODON word
    """
    mean = None
    res = 'Unk'
    valid_scores = [score for score in scores if not math.isnan(score)]
    if valid_scores:
        mean = sum(valid_scores) / len(valid_scores)
    if mean is not None:
        res = SIMPLE_CODON_WORDS[
            bisect.bisect_right(SIMPLE_CODON_MIN, mean) - 1]
    return res

def phylop_subcodon(scores, pos=None):
    """Converts phylop scores from selected codon positions into a
    SIMPLE_CODON word
    """
    mean = None
    res = 'Unk'
    if pos is None:
        pos = [0, 1]
    scorelen = len(scores)
    valid_scores = [scores[i] for i in pos
                    if (i < scorelen and not math.isnan(scores[i]))]
    if valid_scores:
        mean = sum(valid_scores) / len(valid_scores)
    if mean is not None:
        res = SIMPLE_CODON_WORDS[
            bisect.bisect_right(SIMPLE_CODON_MIN, mean) - 1]
    return res
    
def phylop_pos(scores, pos = 0):
    try:
        score = scores[pos]
    except IndexError:
        res = 'Unk'
    else:
        if math.isnan(score):
            res = 'Unk'
        else:
            res = SIMPLE_CODON_WORDS[
                bisect.bisect_right(SIMPLE_CODON_MIN, score) -1]
    return res
