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

# ------------------------------------------------------------------------------
# 
# Author: Gabriele Girelli
# Email: gigi.ga90@gmail.com
# Date: 20171026
# Project: oligo characterization
# Description: correct melting temperature of secondary structures and produce
#              melting curves.
# 
# Changelog:
#  v2.0.0 - 2018-03-28: first release withing pypi package.
# 
# References:
#  [1] McConaughy et al, Biochemistry(8), 1969;
#  [2] Wright et al, Appl. env. microbiol.(80), 2014.
# 
# ------------------------------------------------------------------------------



# DEPENDENCIES =================================================================

import argparse
import os
import re
import sys

import oligo_melting as OligoMelt

# PARAMETERS ===================================================================

# Add script description
parser = argparse.ArgumentParser(
    description = '''
Corrects melting temperature of a secondary structure, predicted with
OligoArrayAux, based on formamide concentration. References:
 [1] McConaughy et al, Biochemistry(8), 1969;
 [2] Wright et al, Appl. env. microbiol.(80), 2014.
''', formatter_class = argparse.RawDescriptionHelpFormatter)

# Add mandatory arguments
parser.add_argument('FILE', type = str, nargs = 1, help = '''
    Tab-separated file with dG, dH, and Tm columns generated by OligoArrayAux.
    Tm is expected to be in Celsius degree, while dH and dG in kcal / mol.''')

# Add arguments with default value
parser.add_argument('-f', '--faconc', type = float, nargs = 1,
    metavar = 'fa_conc', help = '''Formamide concentration in %%(v,v).''',
    default = [0])
parser.add_argument('--t-curve', type = float, nargs = 2,
    metavar = ('range', 'step'), help = '''Temperature range and step for
    melting curve generation. Use --make-curve option to generate the curve.
    Default: 20 degC range and 0.5 degC step.''', default = [20, 0.5])
parser.add_argument('--out-curve', type = str, nargs = 1, metavar = "outname",
    help = '''Path to output table containing tabulated curves.''',
    default = [False])

# Add flags
parser.add_argument('-C', '--celsius',
    dest = 'celsius', action = 'store_const',
    const = True, default = False,
    help = 'Output temperature in Celsius degrees. Default: Kelvin')

# Version flag
version = "2.0.0"
parser.add_argument('--version', action = 'version',
    version = '%s v%s' % (sys.argv[0], version,))

# Parse arguments
args = parser.parse_args()

# Assign to in-script variables ------------------------------------------------

fin_path = args.FILE[0]

# Temperature units
celsius = args.celsius

# Melting curve
curve_range = args.t_curve[0]
curve_step = args.t_curve[1]
curve_outpath = args.out_curve[0]
do_curve = False != curve_outpath

# Formamide
fa_conc = args.faconc[0]

# Additional checks ------------------------------------------------------------

# Check proper curve step/range pair
if curve_step > curve_range / 2:
    sys.exit("!!!ERROR! Curve step must be smaller than curve range.")
curve_range -= curve_range % curve_step

# Remove output curve file if it exists
if do_curve and os.path.isfile(curve_outpath):
    os.remove(curve_outpath)

# FUNCTIONS ====================================================================

# RUN ==========================================================================

# Build argument dictionary
data = {
    'fa_conc' : fa_conc,
    'celsius' : celsius,
    'do_curve' : do_curve,
    'curve_step' : curve_step,
    'curve_range' : curve_range,
    'curve_outpath' : curve_outpath
}

# CALCULATE --------------------------------------------------------------------

if not os.path.exists(fin_path):
    sys.exit("!!!ERROR! File not found: %s" % (fin_path,))
else:
    # Read file
    first_line = True
    with open(fin_path) as fin:
        for row in fin:

            # Parse first line -------------------------------------------------
            if first_line:
                print(row.strip())
                if not "Tm" in row.strip().split("\t"):
                    sys.exit("!!!ERROR! Missing 'Tm' column.")
                else:
                    Tm_col = row.strip().split("\t").index("Tm")
                    dG_col = row.strip().split("\t").index("dG")
                    dH_col = row.strip().split("\t").index("dH")
                    dS_col = row.strip().split("\t").index("dS")
                first_line = False
                continue

            # Parse other lines ------------------------------------------------
            row = row.strip().split("\t")
            oldTm = float(row[Tm_col])
            dG = float(row[dG_col])
            dH = float(row[dH_col])
            dS = float(row[dS_col]) / 1000

            # Correct Tm per formamide
            row[Tm_col] = OligoMelt.SecStr.adj_fa(oldTm + 273.15, fa_conc)
            if celsius:
                row[Tm_col] = row[Tm_col] - 273.15
            row[Tm_col] = "%.1f" % row[Tm_col]

            # Produce melting curves -------------------------------------------
    
            if do_curve:
                fout = open(curve_outpath, 'a+')
                tab = OligoMelt.SecStr.melt_curve(dH, dS, oldTm + 273.15,
                    fa_conc, curve_range, curve_step)
                for (t, k) in tab:
                    if celsius:
                        fout.write("%s\t%f\t%f\n" % (row[0], t - 273.15, k))
                    else:
                        fout.write("%s\t%f\t%f\n" % (row[0], t, k))
                fout.close()

            # Output -----------------------------------------------------------
            print("\t".join(row))

# END ==========================================================================

################################################################################
