#!/usr/bin/env python
# (C) 2017 OpenEye Scientific Software Inc. All rights reserved.
#
# TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
# provided to current licensees or subscribers of OpenEye products or
# SaaS offerings (each a "Customer").
# Customer is hereby permitted to use, copy, and modify the Sample Code,
# subject to these terms. OpenEye claims no rights to Customer's
# modifications. Modification of Sample Code is at Customer's sole and
# exclusive risk. Sample Code may require Customer to have a then
# current license or subscription to the applicable OpenEye offering.
# THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall OpenEye be
# liable for any damages or liability in connection with the Sample Code
# or its use.

from __future__ import print_function
from openeye import oechem
from openeye import oemedchem
import sys
import math


# simple statistics
def average(x):
    return (sum(x) * 1.0 / len(x))


def variance(x):
    return list(map(lambda y: (y - average(x)) ** 2, x))


def stddev(x):
    return math.sqrt(average(variance(x)))


################################################################
# USED TO GENERATE CODE SNIPPETS FOR THE OEMEDCHEM DOCUMENTATION
# - please look at actual examples for useful utility code
################################################################


def ChEMBLSolubilityUsage(itf):
    ifs = oechem.oemolistream()
    if not ifs.open(itf.GetString("-input")):
        oechem.OEThrow.Fatal("Unable to open %s for reading: " +
                             itf.GetString("-input"))

    ofs = oechem.oemolostream()
    if not ofs.open(itf.GetString("-output")):
        oechem.OEThrow.Fatal("Unable to open %s for writing: " +
                             ofs.GetString("-output"))

    oechem.OEThrow.SetLevel(oechem.OEErrorLevel_Warning)

    # @ <SNIPPET-OEAPPLYCHEMBLSOLUBILITY-EXAMPLE>
    # number of bonds of chemistry context at site of change
    #  for the applied transforms
    totalmols = 0
    xformctxt = oemedchem.OEMatchedPairContext_Bond2
    for molidx, mol in enumerate(ifs.GetOEGraphMols(), start=1):
        # consider only the largest input fragment
        oechem.OEDeleteEverythingExceptTheFirstLargestComponent(mol)

        smolcnt = 0
        # only consider solubility transforms having at least 5 matched pairs
        for solMol in oemedchem.OEApplyChEMBL24SolubilityTransforms(mol, xformctxt, 5):
            # compute net change in solubility from MMP data
            deltasol = []
            if oechem.OEHasSDData(solMol, "OEMMP_normalized_value (uM)"):
                for sditem in oechem.OEGetSDData(solMol,
                                                 "OEMMP_normalized_value (uM)").split('\n'):
                    # fromIndex,toIndex,fromValue,toValue
                    sdvalues = sditem.split(',')
                    if not sdvalues[2] or not sdvalues[3]:
                        continue
                    deltasol.append(float(sdvalues[3])-float(sdvalues[2]))
            if not len(deltasol):
                continue

            avgsol = deltasol[0]
            if len(deltasol) > 1:
                avgsol = average(deltasol)

            # reject examples with net decrease in solubility
            if avgsol < 0.0:
                continue
            sdev = stddev(deltasol)

            # annotate with average,stddev,num
            oechem.OEAddSDData(solMol,
                               "OEMMP_average_delta_normalized_value",
                               "{0:.1F},{1:.2F},{2}".format(avgsol, sdev, len(deltasol)))

            # export solubility transformed molecule with SDData annotations
            if oechem.OEWriteMolecule(ofs, solMol) == oechem.OEWriteMolReturnCode_Success:
                smolcnt += 1

        oechem.OEThrow.Info("{0}: Exported molecule count, {1}".format(molidx, smolcnt))
        totalmols += smolcnt
    # @ </SNIPPET-OEAPPLYCHEMBLSOLUBILITY-EXAMPLE>

    print("Exported molecule count = {0}".format(totalmols))

    return True


############################################################
InterfaceData = """
# ChEMBLSolubilityUsage docexample
!CATEGORY ChEMBLSolubilityUsage
    !CATEGORY I/O
        !PARAMETER -input 1
          !ALIAS -i
          !TYPE string
          !REQUIRED true
          !BRIEF Input filename of structures to transform
          !KEYLESS 1
        !END

        !PARAMETER -output 2
          !ALIAS -o
          !TYPE string
          !REQUIRED true
          !BRIEF Output filename for solubility transformed molecules
          !KEYLESS 2
        !END
    !END
!END
"""


def main(argv=[__name__]):
    itf = oechem.OEInterface(InterfaceData, argv)
    ChEMBLSolubilityUsage(itf)


if __name__ == "__main__":
    sys.exit(main(sys.argv))
