from typing import Optional
from est.core.types.xasobject import XASObject
from ewoksorange.gui.orange_imports import gui
from silx.gui import qt
import est.core.io
import est.core.process.ignoreprocess
from est.core.process.io import ReadXasObject
from est.gui.xas_object_definition import XASObjectWindow
from ewoksorange.bindings.owwidgets import OWEwoksWidgetNoThread
from ewokscore.missing_data import is_missing_data
from est.io.io import InputType
from est.io.utils.information import InputInformation
from est.io.utils.url import build_spec_data_url
import h5py

from est.io.utils.url import split_spec_url_elmts


class XASInputOW(OWEwoksWidgetNoThread, ewokstaskclass=ReadXasObject):
    """
    Widget used for signal extraction
    """

    name = "xas input"
    description = "Read .dat file and convert it to spectra"
    icon = "icons/input.png"
    priority = 0
    keywords = ["spectroscopy", "signal", "input", "file"]

    want_main_area = True
    resizing_enabled = True
    want_control_area = False

    def __init__(self):
        super().__init__()
        self._inputWindow = qt.QWidget(parent=self)
        self._inputWindow.setLayout(qt.QGridLayout())

        self._inputDialog = XASObjectWindow(parent=self)
        self._inputWindow.layout().addWidget(self._inputDialog, 0, 0, 1, 2)

        # add the apply button
        types = qt.QDialogButtonBox.Ok
        self._buttons = qt.QDialogButtonBox(parent=self)
        self._buttons.setStandardButtons(types)
        self.layout().addWidget(self._buttons)

        spacer = qt.QWidget(parent=self)
        spacer.setSizePolicy(qt.QSizePolicy.Minimum, qt.QSizePolicy.Expanding)
        self._inputWindow.layout().addWidget(spacer, 2, 0)

        layout = gui.vBox(self.mainArea, "input").layout()
        layout.addWidget(self._inputWindow)

        self.loadSettings(
            input_information=self.get_task_input_value("input_information"),
        )

        # expose api
        self.apply = self.execute_ewoks_task

        # signal / slot connection
        self._buttons.accepted.connect(self.hide)
        self._buttons.accepted.connect(self.execute_ewoks_task)
        self._inputDialog.getMainWindow().editingFinished.connect(self._storeSettings)
        self.setFileSelected = self._inputDialog.setDatFile

    def loadSettings(self, input_information):
        if is_missing_data(input_information):
            return
        elif isinstance(input_information, dict):
            input_information = InputInformation.from_dict(input_information)
        advanceHDF5Info = self._inputDialog.getAdvanceHdf5Information()

        # raise NotImplementedError
        if input_information.spectra_url.file_path() is None:
            input_type = None
        elif h5py.is_hdf5(input_information.spectra_url.file_path()):
            input_type = est.io.InputType.hdf5_spectra
            if input_information.spectra_url is not None:
                self._inputDialog.setSpectraUrl(input_information.spectra_url)
            if input_information.channel_url is not None:
                self._inputDialog.setEnergyUrl(input_information.channel_url)
            if input_information.mu_ref_url is not None:
                advanceHDF5Info.setMuRefUrl(input_information.mu_ref_url)
        elif input_information.spectra_url.file_path().lower().endswith(".xmu"):
            input_type = est.io.InputType.xmu_spectrum
            self._inputDialog.setXmuFile(input_information.spectra_url.file_path())
        else:
            input_type = est.io.InputType.dat_spectrum
            self._inputDialog.setDatFile(input_information.spectra_url.file_path())
            # TODO: check if there is any scan title in here
            urls_with_col_names = {
                self._inputDialog.setEnergyColName: input_information.channel_url,
                self._inputDialog.setAbsColName: input_information.spectra_url,
                self._inputDialog.setMonitorColName: input_information.mu_ref_url,
            }
            for setter, value in urls_with_col_names.items():
                if value is not None:
                    setter(split_spec_url_elmts(value)["col_name"])
            scan_title = split_spec_url_elmts(input_information.spectra_url)[
                "scan_title"
            ]
            if scan_title is not None:
                self._inputDialog.setScanTitle(scan_title)

        if input_information.config_url is not None:
            self._inputDialog.setConfigurationUrl(input_information.config_url)

        if input_information.I0_url is None:
            advanceHDF5Info.setI0Url(input_information.I0_url)
        if input_information.I1_url is None:
            advanceHDF5Info.setI1Url(input_information.I1_url)
        if input_information.I2_url is None:
            advanceHDF5Info.setI2Url(input_information.I2_url)

        if len(input_information.dimensions) == 3:
            self._inputDialog.setDimensions(input_information.dimensions)
        elif not len(input_information.dimensions) == 0:
            raise ValueError("spectra dimensions are expected to be 3D")
        self._inputDialog.getMainWindow().setEnergyUnit(input_information.energy_unit)

        # set up
        self._inputDialog.setCurrentType(input_type)

    def _storeSettings(self):
        self.update_default_inputs(
            input_information=self._getInputInformation().to_dict()
        )

    def _getInputInformation(self):
        input_information = InputInformation(
            spectra_url=self.getSpectraUrl(),
            channel_url=self.getChannelUrl(),
            config_url=self.getConfigurationUrl(),
            dimensions=self.getDimensions(),
            energy_unit=self.getEnergyUnit(),
        )
        # handle extra information like I0...
        advanceHDF5Info = self._inputDialog.getAdvanceHdf5Information()
        input_information.I0_url = advanceHDF5Info.getI0Url()
        input_information.I1_url = advanceHDF5Info.getI1Url()
        input_information.I2_url = advanceHDF5Info.getI2Url()
        input_information.mu_ref_url = advanceHDF5Info.getMuRefUrl()

        return input_information

    def getEnergyUnit(self):
        return self._inputDialog.getMainWindow().getEnergyUnit()

    def getSpectraUrl(self):
        if self._inputDialog.getCurrentType() is InputType.hdf5_spectra:
            return self._inputDialog.getMainWindow().getSpectraUrl()
        else:
            return build_spec_data_url(
                file_path=self._inputDialog.getMainWindow().getDatFile(),
                col_name=self._inputDialog.getMainWindow().getAbsColName(),
                scan_title=self._inputDialog.getMainWindow().getScanTitle(),
            )

    def getChannelUrl(self):
        if self._inputDialog.getCurrentType() is InputType.hdf5_spectra:
            return self._inputDialog.getMainWindow().getEnergyUrl()
        else:
            return build_spec_data_url(
                file_path=self._inputDialog.getMainWindow().getDatFile(),
                col_name=self._inputDialog.getMainWindow().getEnergyColName(),
                scan_title=self._inputDialog.getMainWindow().getScanTitle(),
            )

    def getMonitorUrl(self):
        if self._inputDialog.getCurrentType() is InputType.hdf5_spectra:
            # TODO: case not handled for H5 files ?!!
            return None
        else:
            return build_spec_data_url(
                file_path=self._inputDialog.getMainWindow().getDatFile(),
                col_name=self._inputDialog.getMainWindow().getMonitorColName(),
                scan_title=self._inputDialog.getMainWindow().getScanTitle(),
            )

    def getConfigurationUrl(self):
        return self._inputDialog.getMainWindow().getConfigurationUrl()

    def getDimensions(self):
        return self._inputDialog.getMainWindow().getDimensions()

    def getExtraInfos(self) -> dict:
        if self._inputDialog.getCurrentType() is InputType.hdf5_spectra:
            return {}
        else:
            return {
                "scan_title": self._inputDialog.getMainWindow().getScanTitle(),
            }

    def sizeHint(self):
        return qt.QSize(400, 200)

    def buildXASObj(self) -> Optional[XASObject]:
        return self._inputDialog.buildXASObject()
