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

__authors__ = ["C. Nemoz", "H. Payno"]
__license__ = "MIT"
__date__ = "25/10/2016"

from ..utils import WidgetLongProcessing
from Orange.widgets import widget, gui
from Orange.widgets.settings import Setting
from Orange.widgets.widget import Input, Output
from orangecontrib.tomwer.orange.settings import CallbackSettingsHandler
from tomwer.core.process.reconstruction.ftseries.ftseries import _Ftseries
from tomwer.gui.reconstruction.ftserie import FtserieWidget
from tomwer.core.scan.scanbase import TomwerScanBase
from tomwer.utils import docstring
from ...orange.managedprocess import SuperviseOW
from silx.gui import qt
import copy
import logging

logger = logging.getLogger(__name__)


class FtseriesOW(SuperviseOW, WidgetLongProcessing):
    name = "pyhst2 slice reconstruction"
    id = "orange.widgets.tomwer.ftseries"
    description = "This widget manage the reconstruction of acquisitions"
    icon = "icons/bricks.png"
    priority = 13
    category = "esrfWidgets"
    keywords = ["tomography", "file", "tomwer", "pyhst2"]

    want_main_area = True
    resizing_enabled = True
    allows_cycle = True
    compress_signal = False

    settingsHandler = CallbackSettingsHandler()

    _rpSetting = Setting(dict())

    sigScanReady = qt.Signal()
    """Signal emitted when scan is process"""

    assert len(_Ftseries.inputs) == 2

    class Inputs:
        assert _Ftseries.inputs[0].name == "change recons params"
        recons_param_changed = Input(
            name=_Ftseries.inputs[0].name,
            type=_Ftseries.inputs[0].type,
            doc=_Ftseries.inputs[0].doc,
        )
        data_in = Input(
            name=_Ftseries.inputs[1].name,
            type=_Ftseries.inputs[1].type,
            doc=_Ftseries.inputs[1].doc,
        )

    assert len(_Ftseries.outputs) == 1

    class Outputs:
        data_out = Output(
            name=_Ftseries.outputs[0].name,
            type=_Ftseries.outputs[0].type,
            doc=_Ftseries.outputs[0].doc,
        )

    def __init__(self, parent=None, _connect_handler=True, recons_params=None):
        """
        Widget which read the .hdf5 generated by octave and modify it.
        Then run a subprocess to call octave and run ftseries

        :param bool _connect_handler: True if we want to store the modifications
                                      on the setting. Need for unit test since
                                      keep alive qt widgets.
        :param recons_params: reconsparameter to be used by the FTWidget.
                              If None, some will be created.
        :type: :class:`QReconsParams`
        """
        SuperviseOW.__init__(self, parent)
        WidgetLongProcessing.__init__(self)

        # add the ftserie widget
        self._ftserie = FtserieWidget(
            recons_params=recons_params, process_id=self.process_id
        )
        self._ftserie.hideExecuteButton()

        if self._rpSetting != dict():
            try:
                self._ftserie.recons_params.load_from_dict(self._rpSetting)
            except:
                logger.warning("fail to load reconstruction settings")

        self._ftserie.sigScanReady.connect(self._signalReconsReady)

        _layout = gui.vBox(self.mainArea, self.name).layout()
        _layout.addWidget(self._ftserie)

        # add default buttons
        types = qt.QDialogButtonBox.Ok | qt.QDialogButtonBox.Cancel
        self._controlButtons = qt.QDialogButtonBox(parent=self)
        self._controlButtons.setStandardButtons(types)
        _layout.addWidget(self._controlButtons)
        self._controlButtons.setVisible(False)

        # expose API
        self.updatePath = self._ftserie.updatePath
        self.load = self._ftserie.load
        self.save = self._ftserie.save
        self.getFileEditor = self._ftserie.getReconsParamSetEditor
        self.setH5Exploration = self._ftserie.setH5Exploration
        self.setReconsParams = self._ftserie.set_recons_params

        # connect signal / slot
        if _connect_handler is True:
            self.settingsHandler.addCallback(self._updateSettingsVals)

        # signal / slot connections
        self._ftserie.reconsStack.sigReconsStarted.connect(self.__processing_start)
        self._ftserie.reconsStack.sigReconsFinished.connect(self.__processing_end)
        self._ftserie.reconsStack.sigReconsFailed.connect(self.__processing_end)
        self._ftserie.reconsStack.sigReconsFinished.connect(self._log_succeed)
        self._ftserie.reconsStack.sigReconsFailed.connect(self._log_failed)
        self._ftserie.reconsStack.sigReconsMissParams.connect(self.__processing_end)

        self._controlButtons.accepted.connect(self.accept)
        self._controlButtons.rejected.connect(self.reject_from_dialog)

    def _signalReconsReady(self, scan):
        assert isinstance(scan, TomwerScanBase)
        self.Outputs.data_out.send(scan)
        self.sigScanReady.emit()

    @Inputs.recons_param_changed
    def updateReconsParam(self, ftseries):
        self._ftserie.updateReconsParam(ftseries)
        self._controlButtons.setVisible(True)
        self.show()
        self.exec_()
        self._controlButtons.setVisible(False)

    @Inputs.data_in
    def pathReceived(self, scan):
        if scan is None:
            return
        assert isinstance(scan, TomwerScanBase)
        scan_ = copy.copy(scan)
        scan_.clear_latest_reconstructions()
        self._ftserie.setScan(scan=scan_)
        self._ftserie.pathReceived(scan_)

    @docstring(SuperviseOW)
    def reprocess(self, dataset):
        self.pathReceived(scan=dataset)

    def _updateSettingsVals(self):
        self._rpSetting = self._ftserie.recons_params.to_dict()

    @property
    def recons_params(self):
        return self._ftserie.recons_params

    def accept(self):
        widget.OWWidget.accept(self)
        self._ftserie.process(self._ftserie._scan)

    def reject_from_dialog(self):
        widget.OWWidget.reject(self)
        self._signalReconsReady(self._ftserie._scan)

    def __processing_start(self, scan):
        logger.processStarted(
            "Start pyhst slice reconstruction of {}".format(str(scan))
        )
        self.processing_state(working=True, info=" ")

    def __processing_end(self, scan):
        self.processing_state(working=False, info=" ")

    def _log_succeed(self, scan):
        logger.processSucceed("Pyhst succeed to reconstruct {}".format(str(scan)))

    def _log_failed(self, scan):
        logger.processFailed("Pyhst failed to reconstruct {}".format(str(scan)))

    def close(self):
        self._ftserie.reconsStack.stop()
        super(FtseriesOW, self).close()
