# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2020 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__ = "14/02/2020"


import logging
try:
    from contextlib import AbstractContextManager
except ImportError:
    from tomwer.third_party.contextlib import AbstractContextManager
from silx.gui import qt
from tomwer.synctools.stacks.reconstruction.nabu import NabuProcessStack
import tomwer.core.process.reconstruction.nabu
from tomwer.core.process.reconstruction.nabu import Nabu
from tomwer.gui.reconstruction.nabu.nabu import NabuWindow
from tomwer.core.scan.scanbase import TomwerScanBase
from ..utils import WidgetLongProcessing
from Orange.widgets.settings import Setting
from Orange.widgets import widget, gui
from Orange.widgets.widget import Input, Output
from orangecontrib.tomwer.orange.settings import CallbackSettingsHandler
from tomwer.web.client import OWClient

_logger = logging.getLogger(__name__)


class NabuOW(widget.OWWidget, OWClient, WidgetLongProcessing):
    """
        A simple widget managing the copy of an incoming folder to an other one

        :param parent: the parent widget
        """
    # note of this widget should be the one registered on the documentation
    name = "nabu reconstruction"
    id = "orange.widgets.tomwer.reconstruction.NabuOW.NabuOW"
    description = "This widget will call nabu for running a reconstruction "
    icon = "icons/nabu.svg"
    priority = 12
    category = "esrfWidgets"
    keywords = ["tomography", "nabu", "reconstruction", "FBP", "filter"]

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

    settingsHandler = CallbackSettingsHandler()

    _rpSetting = Setting(dict())

    sigScanReady = qt.Signal(TomwerScanBase)
    "Signal emitted when a scan is ended"

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

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

    class DialogCM(AbstractContextManager):
        """Simple context manager to hida / show button dialogs"""
        def __init__(self, dialogButtonsBox):
            self._dialogButtonsBox = dialogButtonsBox

        def __enter__(self):
            self._dialogButtonsBox.show()

        def __exit__(self, exc_type, exc_val, exc_tb):
            self._dialogButtonsBox.hide()


    process_function = tomwer.core.process.reconstruction.nabu.run_reconstruction

    def __init__(self, parent=None, _connect_handler=True):
        """
        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`
        """
        OWClient.__init__(self, (_logger, ))
        WidgetLongProcessing.__init__(self)
        widget.OWWidget.__init__(self, parent)
        self.__exec_for_ci = False
        # processing tool
        self._processingStack = NabuProcessStack(self)

        _layout = gui.vBox(self.mainArea, self.name).layout()
        # main widget
        self._nabuWidget = NabuWindow(parent=self)
        _layout.addWidget(self._nabuWidget)
        # add button to validate when change reconstruction paramters is called
        types = qt.QDialogButtonBox.Ok
        self._buttons = qt.QDialogButtonBox(self)
        self._buttons.setStandardButtons(types)
        _layout.addWidget(self._buttons)

        # set up
        self._buttons.hide()

        # load settings
        if self._rpSetting != dict():
            try:
                self._nabuWidget.setConfiguration(self._rpSetting)
            except:
                _logger.warning('fail to load reconstruction settings')

        # expose API
        self.getConfiguration = self._nabuWidget.getConfiguration
        self.setConfiguration = self._nabuWidget.setConfiguration

        # connect signal / slot
        self._processingStack.sigComputationStarted.connect(self._startProcessing)
        self._processingStack.sigComputationEnded.connect(self._endProcessing)
        self._buttons.button(qt.QDialogButtonBox.Ok).clicked.connect(self.accept)

        self.settingsHandler.addCallback(self._updateSettingsVals)

    @Inputs.data_in
    def process(self, scan: TomwerScanBase):
        assert isinstance(scan, (TomwerScanBase, type(None)))
        if scan is None:
            return
        self._processingStack.add(scan, self.getConfiguration())

    @Inputs.reprocess
    def reprocess(self, scan):
        """Recompute nabu with different parameters"""
        # wait for user to tune the reconstruction
        if scan is None:
            return

        self.show()
        with NabuOW.DialogCM(self._buttons):
            if self.__exec_for_ci is True:
                self._ciExec()
            else:
                if self.exec_():
                    # for now The behavior for reprocessing is the sama as for processing
                    self.process(scan=scan.instance)

    def _endProcessing(self, scan):
        WidgetLongProcessing._endProcessing(self, scan)
        self.Outputs.data_out.send(scan)
        self.sigScanReady.emit(scan)

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

    def _ciExec(self):
        self.activateWindow()
        self.raise_()
        self.show()

    def _replaceExec_(self):
        """used for CI, replace the exec_ call ny"""
        self.__exec_for_ci = True

    def _updateSettingsVals(self):
        self._rpSetting = self.getConfiguration()
