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

from .      import _Tasks
from ..     import io
from ..data import unitconvert

class SP(_Tasks):
    '''Single-point calculation is the basis of almost all tasks'''

    from ..dl_py2f.utils import miscutils

    _kwargs = {
                'active'   :'all',
                'gradients':(False,''),
                'hessian'  : False
              }

    @miscutils.time
    def run(self, **kwargs):
        '''Run a single-point calculation'''

        from sys    import stdout
        from ..base import finitediff

        if not self.theory:
            print(" ChemShell >>> Please define \"theory\" for single-point evaluation. Exiting...")
            quit(1)

        print("\n >>> Running single point calculation...")

        selectcases = {
                        True :'analytic', # defaults to 'analytic'
                        False:''
                      }

        # first let finite-difference module process the gradients method
        # returned values -1: no gradients
        #                  0: done numerically
        #                  1: analytic method
        greturned = finitediff.calcGradients(self.theory,
                                             method=selectcases.get(self.gradients, self.gradients),
                                             displace=self.displace,
                                             active=self.active,
                                             **kwargs) 

        # hessian calculation driver is DL_FIND
        # returned values -1: no hessians
        #                  0: done numerically
        #                  1: analytic method
        hreturned = finitediff.calcHessian(self.theory,
                                           method=selectcases.get(self.hessian, self.hessian),
                                           displace=self.displace,
                                           active=self.active,
                                           **kwargs)

        # reset the gradients/hessian switches
        selectcases = { -1: False,
                         0: False,
                         1: True }
        self.gradients = selectcases[greturned]
        self.hessian   = selectcases[hreturned]

        # set retart on if finite-difference gradients or Hessians are successfully calculated
        if 0 in [ greturned, hreturned ]:
            self.theory.restart = True

        # simply call theoryObj.run()
        # "gradients" switch belongs to the top Tasks level and it is invisible to the lower Interface level.
        # but we need it to determine for the interface i/o, so we use the internal underlined _gradients for Interfaces.
        # also switch on/off QM/MM gradients merger to avoid erasing after finite-difference gradients
        self.theory.run(_gradients=self.gradients, **kwargs)

        # print the energy
        print("\n " + "-"*70)
        print(" Final SP energy    = %25.12f a.u." % self.result.energy[0])
        print(" " + "-"*70)
        print("")

        return self


# aliases
# FIXME: SyntaxError: can't assign to operator
#Single-Point = SP
