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


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


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

    maxrecs = itf.GetInt("-maxrec")

    oechem.OEThrow.SetLevel(oechem.OEErrorLevel_Warning)

    # @ <SNIPPET-FINDSIMPLEMATCHEDPAIRS-EXAMPLE>
    # create options class with defaults
    mmpOpts = oemedchem.OEMatchedPairAnalyzerOptions()

    # for 'simple' pairs, alter default indexing options
    # - single cuts only, heavy atom substituents only (HMember indexing off)
    mmpOpts.SetOptions(oemedchem.OEMatchedPairOptions_SingleCuts |
                       oemedchem.OEMatchedPairOptions_ComboCuts |
                       oemedchem.OEMatchedPairOptions_UniquesOnly)
    # - limit substituent size to no more than 20% of input structure
    mmpOpts.SetIndexableFragmentRange(80., 100.)

    # create analyzer class with nondefault options
    mmpAnalyzer = oemedchem.OEMatchedPairAnalyzer(mmpOpts)

    # ignore common index status returns
    sIgnoreStatus = 'FragmentRangeFilter,DuplicateStructure,'
    sIgnoreStatus += 'FragmentationLimitFilter,HeavyAtomFilter'

    # index the input structures
    for recindex, mol in enumerate(ims.GetOEGraphMols(), start=1):
        # consider only the largest input fragment
        oechem.OEDeleteEverythingExceptTheFirstLargestComponent(mol)
        # ignore stereochemistry
        oechem.OEUncolorMol(mol,
                            (oechem.OEUncolorStrategy_RemoveAtomStereo |
                             oechem.OEUncolorStrategy_RemoveBondStereo))

        # explicitly provide a 1-based index to refer to indexed structures
        #   - to allow references back to external data elsewhere
        status = mmpAnalyzer.AddMol(mol, recindex)
        if status != recindex:
            if not oemedchem.OEMatchedPairIndexStatusName(status) in sIgnoreStatus:
                oechem.OEThrow.Warning("{0}: molecule indexing error, status={1}"
                                       .format(recindex,
                                               oemedchem.OEMatchedPairIndexStatusName(status)))
        # if limiting input, quit after limit
        if maxrecs and recindex >= maxrecs:
            break

    print("Index complete, matched pairs = {0}"
          .format(mmpAnalyzer.NumMatchedPairs()))

    # specify how transforms are extracted (direction and allowed properties)
    extractMode = (oemedchem.OEMatchedPairTransformExtractMode_Sorted |
                   oemedchem.OEMatchedPairTransformExtractMode_NoSMARTS |
                   oemedchem.OEMatchedPairTransformExtractMode_AddMCSCorrespondence)

    extractOptions = oemedchem.OEMatchedPairTransformExtractOptions()
    # specify amount of chemical context at the site of the substituent change
    #   in the transform
    extractOptions.SetContext(oemedchem.OEMatchedPairContext_Bond0)
    extractOptions.SetOptions(extractMode)

    # walk the transforms and print the matched pairs
    xfmidx = 0
    for mmpxform in oemedchem.OEMatchedPairGetTransforms(mmpAnalyzer,
                                                         extractOptions):
        xfmidx += 1
        print("{0:2} {1}".format(xfmidx, mmpxform.GetTransform()))
        # dump matched molecular pairs and index identifiers
        #   (recindex from indexing loop above)
        for mmppair in mmpxform.GetMatchedPairs():
            print("\tmatched pair molecule indices=({0},{1})".format(mmppair.FromIndex(),
                                                                     mmppair.ToIndex()))
    # @ </SNIPPET-FINDSIMPLEMATCHEDPAIRS-EXAMPLE>

    return True


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

        !PARAMETER -output 2
          !ALIAS -o
          !TYPE string
          !BRIEF Output filename for simple pair transformations
        !END

        !PARAMETER -maxrec 3
          !TYPE int
          !DEFAULT 0
          !BRIEF Only process maxrec records from input file
        !END
    !END
!END
"""


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


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