# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ###########################################################################*/
"""module for process base class"""

__authors__ = ["H. Payno"]
__license__ = "MIT"
__date__ = "07/08/2019"


from .progress import Progress
from est.core.types import XASObject
import logging
_logger = logging.getLogger(__name__)


_DEBUG = True
if _DEBUG:
    from est.core.types import Spectrum
    import numpy


class Process(object):
    def __init__(self, name):
        assert type(name) is str
        self._name = name
        self._advancement = Progress(name=name)
        self.__stop = False
        """flag to notice when a end of process is required"""
        self._settings = {}
        # configuration
        self._callbacks = []

    @property
    def name(self):
        return self._name

    def stop(self):
        self.__stop = True

    @property
    def advancement(self):
        return self._advancement

    @advancement.setter
    def advancement(self, advancement):
        assert isinstance(advancement, Progress)
        self._advancement = advancement

    @property
    def callbacks(self):
        return self._callbacks

    @staticmethod
    def getXasObject(xas_obj):
        if isinstance(xas_obj, dict):
            _xas_obj = XASObject.from_dict(xas_obj)
        else:
            _xas_obj = xas_obj
        assert isinstance(_xas_obj, XASObject)
        if _xas_obj.n_spectrum > 0:
            for spectrum in _xas_obj.spectra:
                assert isinstance(spectrum, Spectrum)
            assert isinstance(spectrum.energy, numpy.ndarray)
            assert isinstance(spectrum.mu, numpy.ndarray)
        assert isinstance(_xas_obj, XASObject)
        return _xas_obj

    def program_name(self):
        """Name of the program used for this processing"""
        raise NotImplementedError('Base class')

    def program_version(self):
        """version of the program used for this processing"""
        raise NotImplementedError('Base class')

    def definition(self):
        """definition of the process"""
        raise NotImplementedError('Base class')

    def getConfiguration(self):
        """

        :return: configuration of the process
        :rtype: dict
        """
        if len(self._settings) > 0:
            return self._settings
        else:
            return None

    def setConfiguration(self, configuration):
        self._settings = configuration

    def register_process(self, xas_obj, data_keys):
        """
        Store the current process in the linked h5 file if any,
        output data stored will be the one defined by the data_keys

        :param xas_obj: object for which we want to save the treatment
        :type: :class:`.XASObject`
        :param tuple data_keys: keys of the id to save
        :param str relative_to: x value
        """
        if xas_obj.has_linked_file():
            _data = {}
            for key in data_keys:
                relative_to = None
                if key in ('flat', 'fpp', 'f2', 'dmude', 'norm', 'norm_area',
                           'post_edge', 'bkg', 'energy', 'mback_mu',
                           'norm_mback', 'Energy', 'Mu', "NormalizedEnergy",
                           "NormalizedMu", "NormalizedSignal", "EXAFSKValues",
                           "EXAFSSignal"):
                    relative_to = 'energy'
                    use = '_spectra_volume'
                elif key in ('chir_re', 'chir_im', 'chir_mag', 'r'):
                    relative_to = 'r'
                    use = '_spectra_volume'
                elif key in ('ft.radius', 'ft.intensity', 'ft.imaginary'):
                    relative_to = 'radius'
                    use = '_list_res_ft'
                if use == '_spectra_volume':
                    # if we can display the result as a numpy.array 3d
                    try:
                        _data[key] = XASObject._spectra_volume(xas_obj.spectra,
                                                               key=key,
                                                               dim_1=xas_obj.dim1,
                                                               dim_2=xas_obj.dim2,
                                                               relative_to=relative_to)
                    except KeyError:
                        mess = '%s: unable to store %s, parameter not found' % (self.name, key)
                        _logger.error(mess)

                elif use == '_list_res_ft':
                    res = []
                    key_ = key.split('.')[-1]
                    for spectrum in xas_obj.spectra:
                        res.append(getattr(spectrum.ft, key_))
                    _data[key] = res
                else:
                    raise ValueError()
            xas_obj.register_processing(process=self, data=_data)

    def addCallback(self, callback):
        self._callbacks.append(callback)