#  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 chemsh
from .               import _Interfaces
from ..base          import run
from ..io            import file, warnings
from ..dl_py2f.utils import fileutils, miscutils, modutils

# inherit <ctypes.Structure> type for convenience of making callback
class _Driver(_Interfaces):
    '''Parent class of driver-type interfaces, such as DL_MONTE'''

    _kwargs = {
                'path'       :'',
                'sysmpi'     :'',
                'temperature': 293.15,
              }

    _internals = {
                   '_syscmd': ''
                 }


    # called by run.runExec() or run.runLib()
    def genInput(self, **kwargs):
        '''Generate input'''

        # single input file
        if type(self.input) in [ str ]:
            fileutils.write(self.input, self.getInpBuff(**kwargs))

        # multiple input files
        elif type(self.input) in [ list, tuple ]:
            for input in self.input:
                fileutils.write(input, self.getInpBuff(input=input, **kwargs))


    def parseMainOutput(self):
        '''Parse main output file self.output'''

        self.setOutKeys()

        self._result.energy    = file.getArrayFromFile(self.output, 'out', self._result.energy.shape   , regex=self._outkeys['energy'])
        self._result.gradients = file.getArrayFromFile(self.output, 'out', self._result.gradients.shape, regex=self._outkeys['gradients'])


    def parseAdditionalOutput(self):
        '''Parse additional output data, e.g., nwchem.bq_grads. This procedure could be overloaded, otherwise ignored.'''


    def setOutKeys(self):
        '''Set up {task:keyword} for output parsing. Shall be overloaded if needed.'''


    def parseOutput(self):
        '''Parse output'''

        self.parseMainOutput()

        self.parseAdditionalOutput()


    @miscutils.time
    def run(self, **kwargs):
        '''Single-point procedure'''

        from sys import stderr, stdout

        # YL: we allow users to override linked-in libs with `path`: check path first then isLinked then _syscmd
        cmdpath = self.path or modutils.getLinkedLib(self.__class__.__name__, chemsh.CHEMSH_LIB_DIR) or self._syscmd
        if chemsh.CHEMSH_DEBUG > 2:
            print(" >>> ChemShell module {} execution path: {}".format(self.__class__.__name__, cmdpath))

        # don't use higher level task routines here (need to keep lower level separate)
        # all keyword arguments at the run() step will be temporary
        return run.run(self, cmdpath, **kwargs)

        # flush output
        stderr.flush()
        stdout.flush()


    @property
    def runargs(self):
        ''''''

        try:
            return self._runargs
        except:
            # default is just self.input
            return ''

    @runargs.setter
    def runargs(self, val):
        '''Setter of runargs'''

        self._runargs = val


    @property
    def restart(self):
        '''Restart from a previous calculation'''

        return self._restart


    @restart.setter
    def restart(self, val):
        '''Setter of restart'''

        self._restart = val


    @property
    def _underscored_name(self):
        ''''''

        return '_' + self.__class__.__name__.lower()


    @property
    def _extrafiles(self):
        '''Additional output files'''

        return []


    @property
    def path(self):
        '''Path of command to execute the code'''

        return self._path


    @path.setter
    def path(self, val):
        '''Setter of path'''

        self._path = val


