#!/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 sys

import oligo_melting as OligoMelt  # type: ignore
from oligo_melting.const import __version__  # type: ignore

# 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
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 = curve_outpath is not False

# 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 "Tm" not 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 ==========================================================================

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