'''Interface to DL_FIND'''

#  Copyright (C) 2017 The authors of Py-ChemShell
#
#  This file is part of Py-ChemShell.
#
#  Py-ChemShell is free software: you can redistribute it and/or modify
#  it under the terms of the GNU Lesser General Public License as
#  published by the Free Software Foundation, either version 3 of the
#  License, or (at your option) any later version.
#
#  Py-ChemShell is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with Py-ChemShell.  If not, see
#  <http://www.gnu.org/licenses/>.

import os.path
import chemsh
from ctypes          import byref, c_long, CDLL
# for convenient importing only (for example in <class Opt>)
from .callback       import callback, setParameters
from ..dl_py2f       import py2f, utils
from ..dl_py2f.utils import miscutils

# use cross-platform function os.path.join() 
soname = os.path.join(chemsh.CHEMSH_LIB_DIR, 'libdlf.so')
libdlf = CDLL(soname)


@miscutils.time
def run(theoryObj, optionsObj, dryrun=False, **kwargs):
    '''Call the Fortran codes of DL_FIND'''

    from sys    import stdout
    from ..base import errors

    natoms = theoryObj.frag.natoms
    # constraints used with HDLC coordinates (only)
    ncons = optionsObj.ncons

    nconns  = 0      # TODO: handle additional connections
    nmasses = natoms
    n_po_scaling = 0

    # parameterisation mode
    if optionsObj.parameterise:
        nvarin  = optionsObj.parameters.size
        nvarin2 = nvarin
        nweight = 0
        # DL_FIND hardcodes nat=nvarin/3
        nspec  = int(nvarin/3)*2

    # atomic mode
    else:
        nz      = natoms 
        nweight = 0
        nvarin  = natoms*3
    #    nvarin2 = nvarin*optionsObj.nframes + nweight + nmasses + n_po_scaling
        nvarin2 = 3*natoms*optionsObj.nframes + natoms
        nspec  = natoms + nz + 5*ncons + 2*nconns + natoms

    if optionsObj.lbfgs_mem < 0:
        optionsObj.lbfgs_mem = max([nvarin, 5])
        optionsObj.lbfgs_mem = min([nvarin,50])

    theoryRef  = py2f(theoryObj        , byref=True, debug=chemsh.CHEMSH_DEBUG)
    fragRef    = py2f(theoryObj.frag   , byref=True, debug=chemsh.CHEMSH_DEBUG)
    resultRef  = py2f(theoryObj._result, byref=True, debug=chemsh.CHEMSH_DEBUG)
    optionsRef = py2f(optionsObj       , byref=True, debug=chemsh.CHEMSH_DEBUG)

    ierror = libdlf.interface_dlf(byref(c_long(nvarin)),          # nvarin : number of variables to read in: 3*natoms
                                  byref(c_long(nvarin2)),         # nvarin2: number of variables to read in in the second array (coords2)
                                  byref(c_long(nspec)),          # nspec  : number of values in the integer array spec
                                  theoryRef,
                                  fragRef,
                                  resultRef,
                                  optionsRef)
# YL 26/01/2021: removed the old callback functions containing global variables
#                new callbacks are passed with optionsObj now
#                                  byref(Callback(callback)))

    # at last, move NEB files to _dl_find
    utils.fileutils.archiveFile(['neb_{:04d}.xyz'.format(i+1) for i in range(optionsObj.nimages)], dir='_dl_find', suffix='')
    utils.fileutils.archiveFile(['path.xyz','path_active.xyz','path_force.xyz','path_tsmode.xyz'], dir='_dl_find', suffix='')

    if chemsh.CHEMSH_DEBUG > 2:
        print("\n >>> ChemShell DL_FIND: ierror = {}".format(ierror))
    stdout.flush()

    return ierror


