# 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 orangewidget import widget, gui
from orangewidget.settings import Setting
from orangewidget.widget import Input, Output
from orangecontrib.tomwer.orange.settings import CallbackSettingsHandler
from tomwer.gui.reconstruction.ftserie import FtserieWidget
from tomwer.core.scan.scanbase import TomwerScanBase
from tomwer.core.scan.scanbase import _TomwerBaseDock
from tomwer.utils import docstring
import tomwer.core.process.reconstruction.ftseries.ftseries
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
    keywords = ["tomography", "file", "tomwer", "pyhst2"]

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

    ewokstaskclass = tomwer.core.process.reconstruction.ftseries.ftseries.Ftseries

    settingsHandler = CallbackSettingsHandler()

    _rpSetting = Setting(dict())
    # this one is keep for backward compatibility. Will be removed on 0.10 I guess

    static_input = Setting({"data": None, "pyhst2_params": None})

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

    class Inputs:
        recons_param_changed = Input(
            name="change recons params",
            type=_TomwerBaseDock,
        )

        data = Input(name="data", type=TomwerScanBase, doc="one scan to be process")

    class Outputs:
        data = Output(name="data", type=TomwerScanBase, doc="one scan to be process")

    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()

        pyhst2_params = self.static_input.get("pyhst2_params", None)
        if pyhst2_params is None:
            pyhst2_params = self._rpSetting
        if pyhst2_params != dict():
            try:
                self._ftserie.recons_params.load_from_dict(pyhst2_params)
            except Exception:
                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_recons_params
        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._reconstruction_stack.sigReconsStarted.connect(
            self.__processing_start
        )
        self._ftserie._reconstruction_stack.sigReconsFinished.connect(
            self.__processing_end
        )
        self._ftserie._reconstruction_stack.sigReconsFailed.connect(
            self.__processing_end
        )
        self._ftserie._reconstruction_stack.sigReconsFinished.connect(self._log_succeed)
        self._ftserie._reconstruction_stack.sigReconsFailed.connect(self._log_failed)
        self._ftserie._reconstruction_stack.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.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
    def pathReceived(self, scan):
        if scan is None:
            return
        if not isinstance(scan, TomwerScanBase):
            raise TypeError(f"{scan} is expected to be an instance of {TomwerScanBase}")
        scan_ = copy.copy(scan)
        scan_.clear_latest_reconstructions()

        self._ftserie.setScan(scan=scan_)
        self._ftserie.process(scan_)

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

    def _updateSettingsVals(self):
        self._rpSetting = self._ftserie.recons_params.to_dict()
        # keep backward compatibility
        self.static_input = {
            "data": None,
            "pyhst2_params": self._ftserie.recons_params.to_dict(),
        }

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

    def accept(self):
        widget.OWBaseWidget.accept(self)
        self._ftserie.process(self.scan)

    def reject_from_dialog(self):
        widget.OWBaseWidget.reject(self)
        self._signalReconsReady(self.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._reconstruction_stack.stop()
        super(FtseriesOW, self).close()

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

    def load_recons_params(self, params):
        return self._ftserie.load_recons_params(params)

    def setDryRun(self, dry_run):
        self._ftserie.setDryRun(dry_run)
