# 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__ = ["H. Payno"]
__license__ = "MIT"
__date__ = "11/03/2020"

import os
import logging
from copy import copy
from silx.gui import qt
from orangewidget import gui
from tomwer.gui.control.datalist import BlissHDF5DataListMainWindow
from orangecontrib.tomwer.orange.managedprocess import TomwerWithStackStack
from tomwer.core.scan.hdf5scan import HDF5TomoScan, HDF5TomoScanIdentifier
from tomwer.core.process.control.nxtomomill import H5ToNxProcess
from orangecontrib.tomwer.widgets.control.NXTomomillMixIn import NXTomomillMixIn
from tomwer.core.scan.blissscan import BlissScan
from nxtomomill.io.config import TomoHDF5Config as HDF5Config
from tomwer.gui.control.nxtomomill import OverwriteMessage
from tomwer.gui.control.nxtomomill import NXTomomillInput

logger = logging.getLogger(__name__)


class NXTomomillOW(
    TomwerWithStackStack,
    NXTomomillMixIn,
    ewokstaskclass=H5ToNxProcess,
):
    """
    Widget to allow user to pick some bliss files and that will convert them
    to HDF5scan.
    """

    name = "bliss(HDF5)-nxtomomill"
    id = "orange.widgets.tomwer.control.NXTomomillOW.NXTomomillOW"
    description = (
        "Read a bliss .h5 file and extract from it all possible"
        "NxTomo. When validated create a TomwerBaseScan for each "
        "file and entry"
    )
    icon = "icons/nxtomomill.svg"
    priority = 120
    keywords = [
        "hdf5",
        "nexus",
        "tomwer",
        "file",
        "convert",
        "NXTomo",
        "tomography",
        "nxtomomill",
    ]

    want_main_area = True
    want_control_area = False
    resizing_enabled = True
    compress_signal = False

    CONFIG_CLS = HDF5Config

    LOGGER = logger

    def __init__(self, parent=None):
        TomwerWithStackStack.__init__(self, parent=parent)
        NXTomomillMixIn.__init__(self)
        _layout = gui.vBox(self.mainArea, self.name).layout()

        self.widget = BlissHDF5DataListMainWindow(parent=self)
        _layout.addWidget(self.widget)
        self.__request_input = True
        # do we ask the user for input if missing
        self._inputGUI = None
        """Gui with cache for missing field in files to be converted"""
        self._canOverwriteOutputs = False
        """Cache to know if we have to ask user permission for overwriting"""

        # expose API
        self.n_scan = self.widget.n_scan
        # alias used for the 'simple workflow' for now
        self.start = self._sendAll

        # connect signal / slot
        self.widget._sendButton.clicked.connect(self._sendAll)
        self.widget._sendSelectedButton.clicked.connect(self._sendSelected)
        self.widget.sigNXTomoCFGFileChanged.connect(self._saveNXTomoCfgFile)
        self.widget.sigUpdated.connect(self._updateSettings)

        # set default configuration is no existing configuration file defined in the settings
        self.update_default_inputs(
            h5_to_nx_configuration=HDF5Config().to_dict(),
        )
        self.task_output_changed_callbacks.add(self._notify_state)

        # handle settings
        self._loadSettings()

    def _updateSettings(self):
        self._scans = []
        for scan in self.widget.datalist._myitems:
            # kept for backward compatibility since 0.11. To be removed on the future version.
            if "@" in scan:
                entry, file_path = scan.split("@")
                hdf5_tomo_scan = HDF5TomoScan(entry=entry, scan=file_path)
                self.add(hdf5_tomo_scan)
            else:
                self._scans.append(scan)
        self._ewoks_default_inputs[  # pylint: disable=E1137
            "output_dir"
        ] = self.widget.getOutputFolder()

    @property
    def request_input(self):
        return self.__request_input

    @request_input.setter
    def request_input(self, request):
        self.__request_input = request

    def get_task_inputs(self):
        return {
            "h5_to_nx_configuration": self.__configuration_cache.to_dict(),
        }

    def handleNewSignals(self) -> None:
        """Invoked by the workflow signal propagation manager after all
        signals handlers have been called.
        """
        # for now we want to avoid propagation any processing.
        # task will be executed only when the user validates the dialog
        h5_scan = super().get_task_inputs().get("hdf5_scan", None)
        if h5_scan is not None:
            if not isinstance(h5_scan, H5ToNxProcess):
                raise TypeError(
                    "hdf5_scan is expected to be an instance of HDF5TomoScan"
                )
            self.add(h5_scan.path)

    def _convertAndSend(self, bliss_url):
        """

        :param str bliss_url: string at entry@file format
        """
        logger.processStarted("Start translate {} to NXTomo".format(str(bliss_url)))
        self.__configuration_cache = HDF5Config.from_dict(
            copy(self.get_default_input_values()["h5_to_nx_configuration"])
        )

        identifier = HDF5TomoScanIdentifier.from_str(bliss_url)
        bliss_scan = BlissScan(
            master_file=identifier.file_path,
            entry=identifier.data_path,
            proposal_file=None,
        )

        output_file_path = H5ToNxProcess.deduce_output_file_path(
            bliss_scan.master_file,
            entry=bliss_scan.entry,
            outputdir=self.widget.getOutputFolder(),
            scan=bliss_scan,
        )

        self.__configuration_cache.input_file = bliss_scan.master_file
        self.__configuration_cache.output_file = output_file_path
        self.__configuration_cache.entries = (bliss_scan.entry,)
        self.__configuration_cache.single_file = False
        self.__configuration_cache.overwrite = True
        self.__configuration_cache.request_input = self.request_input
        self.__configuration_cache.file_extension = ".nx"

        self._processBlissScan(bliss_scan)

    def _userAgreeForOverwrite(self, file_path):
        if self._canOverwriteOutputs:
            return True
        else:
            msg = OverwriteMessage(self)
            text = "NXtomomill will overwrite \n %s. Do you agree ?" % file_path
            msg.setText(text)
            if msg.exec_():
                self._canOverwriteOutputs = msg.canOverwriteAll()
                return True
            else:
                return False

    def _processBlissScan(self, bliss_scan):
        if bliss_scan is None:
            return
        output_file_path = H5ToNxProcess.deduce_output_file_path(
            bliss_scan.master_file,
            entry=bliss_scan.entry,
            outputdir=self.widget.getOutputFolder(),
            scan=bliss_scan,
        )
        # check user has rights to write on the folder
        dirname = os.path.dirname(output_file_path)
        if os.path.exists(dirname) and not os.access(dirname, os.W_OK):
            msg = qt.QMessageBox(self)
            msg.setIcon(qt.QMessageBox.Warning)
            text = f"You don't have write rights on '{dirname}'. Unable to generate the nexus file associated to {str(bliss_scan)}"
            msg.setWindowTitle("No rights to write")
            msg.setText(text)
            msg.show()
            return
        # check if need to overwrite the file
        elif os.path.exists(output_file_path):
            if not self._userAgreeForOverwrite(output_file_path):
                return

        # keep 'h5_to_nx_configuration' up to date according to input folder and output_file updates
        self.update_default_inputs(
            h5_to_nx_configuration=self.__configuration_cache.to_dict()
        )
        self._execute_ewoks_task(propagate=True)

    def _loadSettings(self):
        for scan in self._scans:
            assert isinstance(scan, str)
            try:
                self.widget.add(scan)
            except Exception as e:
                logger.error("Fail to add {}. Error is {}".format(scan, e))
            else:
                logger.warning("{} is an invalid link to a file".format(scan))
        if "nxtomomill_cfg_file" in self._ewoks_default_inputs:  # pylint: disable=E1135
            nxtomo_cfg_file = self._ewoks_default_inputs[  # pylint: disable=E1136
                "nxtomomill_cfg_file"
            ]
            self.widget.setCFGFilePath(nxtomo_cfg_file)
        if "output_dir" in self._ewoks_default_inputs:  # pylint: disable=E1135
            self.widget.setOutputFolder(
                self._ewoks_default_inputs["output_dir"]  # pylint: disable=E1136
            )

    def getHDF5Config(self):
        configuration_file = self.widget.getCFGFilePath()
        if configuration_file in (None, ""):
            configuration = HDF5Config()
        else:
            try:
                configuration = HDF5Config.from_cfg_file(configuration_file)
            except Exception as e:
                logger.error(
                    "Fail to use configuration file {}."
                    "Error is {}. No conversion will be done."
                    "".format(configuration_file, e)
                )
                return None
        return configuration

    def _saveNXTomoCfgFile(self, cfg_file):
        super()._saveNXTomoCfgFile(cfg_file, keyword="h5_to_nx_configuration")

    def _sendSelected(self):
        """Send a signal for selected scans found to the next widget"""
        self._inputGUI = NXTomomillInput()
        # reset the GUI for input (reset all the cache for answers)
        self._canOverwriteOutputs = False
        for bliss_url in self.widget.datalist.selectedItems():
            identifier = HDF5TomoScanIdentifier.from_str(bliss_url.text())
            self._inputGUI.setBlissScan(
                entry=identifier.data_path, file_path=identifier.file_path
            )
            self._convertAndSend(bliss_url.text())

    def _sendAll(self):
        """Send a signal for each scan found to the next widget"""
        self._inputGUI = NXTomomillInput()
        # reset the GUI for input (reset all the cache for answers)
        self._canOverwriteOutputs = False
        for bliss_url in self.widget.datalist.selectedItems():
            identifier = HDF5TomoScanIdentifier.from_str(bliss_url.text())
            self._inputGUI.setBlissScan(
                entry=identifier.data_path, file_path=identifier.file_path
            )
            self._convertAndSend(bliss_url.text())

    def _notify_state(self):
        try:
            task_executor = self.sender()
            task_suceeded = task_executor.succeeded
            config = task_executor.current_task.inputs.h5_to_nx_configuration
            config = HDF5Config.from_dict(config)
            scan = task_executor.current_task.outputs.data
            if task_suceeded:
                self.notify_succeed(scan=scan)
            else:
                self.notify_failed(scan=scan)
        except Exception as e:
            logger.error(f"failed to handle task finished callback. Raiseon is {e}")
