'''Running methods for tasks'''

#  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 ..io            import keyword
from ..objects       import options, result
from ..dl_py2f.utils import objutils

# not a <class ctypes.Structure> type any more since we do not pass it to Fortran, we pass Task.options instead
class _Tasks:
    ''''''

    _kwargs = {
                'displace'   : 1.0E-5,             # displacement (step size) of finite-difference gradients 
                'frag'       : None,               # YL TODO: this should be a ref to self.theory.frag
                'frag2'      : None,
                'gradients'  : False,
                'options'    : options.Options(),
                'result'     : result.Result(),
                'temperature': 300.0,
                'theory'     : None,
              }

    _internals  = {}

    _inherit    = '_init', '_priorities'

    _init       = '_kwargs', '_internals'

    _priorities = 'theory'

#    _excluded = 'theory', 'options', 'result'



    @objutils.init()
    def __init__(self, **kwargs):
        '''Instantiate'''

        # shall NOT be added via '_kwargs'!
        # reason: an instance of children class (e.g., <Opt>) inherits '_kwargs' from
        #         <Task> (utils.updateAttrs()), however attribute 'options' is required
        #         prior to this since instance's attributes will be synchronised to 'options'

#        utils.objutils.initAttrs(self)

        keyword.parseKeys(self, **kwargs)


    @property
    def result(self):
        '''Result'''

        # `result` depends on `theory`
#        self._result.natoms = self._theory.frag.natoms
        self._result.znums  = self._theory.frag.znums

        return self._result

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

        self._result = val


    @property
    def theory(self):
        '''Theory'''

        return self._theory


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

        self._theory = val

        # create a ref to Task.result in the theory object
        if self._theory:
            self._theory._result = self.result


    @property
    def options(self):
        '''Options'''
  
        for key, val in self._kwargs.items():

            # skip attributes in [ 'theory', 'options', 'result' ]:
            if key in [ 'theory', 'options', 'result' ]:
                continue

            # complement options._kwargs
            self._options._kwargs.setdefault(key, val)

            # sync the value
            setattr(self._options, key, getattr(self, key))

        return self._options


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

        self._options = val


    @property
    def __all__(self):
        '''__all__ inherited from base class(es)'''

        # __self_class__ is the children class (i.e. <class Opt>)
        lbuff = list(super().__self_class__._kwargs.keys())

        # __thisclass__ is the base class (i.e. <class Task>)
        lbuff.extend(super().__thisclass__._kwargs)

        __all__ = list(set(lbuff))

        __all__.sort()

        return __all__


#    __all__ = list(_kwargs.keys())

    def _setattr(self, attr, val):
        '''Set value to attribute iff the current equals the default'''

        if getattr(self, attr) == self._kwargs[attr]:
            setattr(self, attr, val)

