from PyQt5.QtWidgets import QVBoxLayout, QMessageBox, QWidget, QPushButton
from .file_selector_combobox import get_file_selector_combobox_using_settings
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QMetaObject
from ..python_core.p1_class import Default_P1_Getter, P1SingleWavelengthTIF, \
    P1SingleWavelengthLSM, P1DualWavelengthTill, P1SingleWavelengthTill, P1DualWavelengthTIFSingleFile, \
    P1SingleWavelengthLIF, P1SingleWavelength666, get_empty_p1
from view.python_core.flags import FlagsManager
import pathlib as pl
import traceback
import sys
from abc import abstractmethod
import tempfile


# modified version of solution from https://stackoverflow.com/questions/9374063/remove-all-items-from-a-layout
def clear_layout(layout):
    if layout is not None:
        while layout.count():
            item = layout.takeAt(0)
            widget = item.widget()
            if widget is not None:
                widget.setParent(None)
            else:
                clear_layout(item.layout())


class DirectDataLoader(QWidget):

    data_loaded_signal = pyqtSignal(dict, FlagsManager)

    def __init__(self, parent, default_LE_loadExp=3):

        super().__init__(parent=parent)
        vbox = QVBoxLayout(self)
        self.refresh_layout(default_LE_loadExp)

    @pyqtSlot(int)
    def refresh_layout(self, LE_loadExp):

        loader_interface_class = get_loader_interface_class(LE_loadExp)
        self.loader_interface = loader_interface_class(self)
        self.loader_interface.refresh_layout(self)
        self.loader_interface.data_loaded_signal.connect(self.data_loaded_signal)


def get_a_pst_combobox(parent, multiple_selection_allowed=True):
    combobox_class = get_file_selector_combobox_using_settings(multiple_selection_allowed=multiple_selection_allowed)

    return combobox_class(parent=parent,
                          groupbox_title="Till Vision Raw Data File(s)",
                          use_list_in_settings="raw data files",
                          settings_list_value_filter=lambda x: x.endswith(".pst"),
                          default_directory=None,
                          file_type="PST",
                          file_filter="PST files(*.pst)",
                          comment=None)


def get_an_lsm_combobox(parent):
    combobox_class = get_file_selector_combobox_using_settings(multiple_selection_allowed=True)

    return combobox_class(parent=parent,
                          groupbox_title="Zeiss Raw Data File(s)",
                          use_list_in_settings="raw data files",
                          settings_list_value_filter=lambda x: x.endswith(".lsm"),
                          default_directory=None,
                          file_type="LSM",
                          file_filter="LSM files(*.lsm)",
                          comment=None)


def get_a_lif_combobox(parent):
    combobox_class = get_file_selector_combobox_using_settings(multiple_selection_allowed=True)

    return combobox_class(parent=parent,
                          groupbox_title="Leica .lif Data File(s)",
                          use_list_in_settings="raw data files",
                          settings_list_value_filter=lambda x: x.endswith(".lif"),
                          default_directory=None,
                          file_type="Lif",
                          file_filter="Llif files(*.lif)",
                          comment=None)


def get_a_tiff_combobox(parent):
    combobox_class = get_file_selector_combobox_using_settings(multiple_selection_allowed=True)

    return combobox_class(parent=parent,
                          groupbox_title="VIEW-tif File(s)",
                          use_list_in_settings="raw data files",
                          settings_list_value_filter=lambda x: x.endswith(".tif") or x.endswith(".tiff"),
                          default_directory=None,
                          file_type="TIF",
                          file_filter="TIF files(*.tif *.tiff)",
                          comment=None)


class BaseLoaderInterface(QObject):

    data_loaded_signal = pyqtSignal(dict, FlagsManager)

    def __init__(self, parent):

        super().__init__(parent)
        self.default_p1_getter = Default_P1_Getter()

    def write_status(self, msg):

        self.parent().parent().parent().parent().write_status(msg)

    @pyqtSlot(list)
    @pyqtSlot(str)
    def load_list(self, filenames):

        filenames = self.check_revise_filenames(filenames)
        temp_dir = pl.Path(filenames[0][0]).parent / "temp_dir_for_view_analyses"
        temp_dir.mkdir(exist_ok=True)

        if filenames is not None:
            current_flags = self.parent().parent().parent().parent().flags.copy()
            # by default, compound path flags are not set, so set to parent of the raw files
            for flag_name in current_flags.compound_path_flags:
                current_flags.update_flags({flag_name: str(temp_dir)})
            current_flags.update_flags({"STG_Datapath": str(temp_dir.parent)})

            label_p1_mapping = {}
            for filename in filenames:
                p1 = self.check_read_data(current_flags, filename)
                if p1 is not None:
                    p1.calculate_signals(current_flags)
                    label_p1_mapping[p1.metadata.ex_name] = p1

            if label_p1_mapping:
                self.data_loaded_signal.emit(label_p1_mapping, current_flags)

    def check_read_data(self, flags, filenames):

        p1 = get_empty_p1(LE_loadExp=flags["LE_loadExp"], odor_conc=10)  # here p1 has not data, i.e. empty
        self.write_status(f"[working] Loading raw data directly from {filenames} using {p1.__class__.__name__}")
        try:
            p1.load_without_metadata(filenames=filenames, flags=flags)
        except Exception as e:
            exception_formatted = traceback.format_exception(*sys.exc_info())
            QMessageBox.critical(self.parent(), "Error reading file",
                                 f"Please check {filenames}.\n\n"
                                 f"Complete error message:\n"
                                 f"\n{''.join(exception_formatted)}")
            self.write_status(f"[failure] Loading raw data directly from {filenames}")
            return None

        self.write_status(f"[success] Loading raw data directly from {filenames}")
        return p1

    def check_revise_filenames(self, filenames):

        return [[x] for x in filenames]

    @abstractmethod
    def refresh_layout(self, widget):
        pass


class SampleData666LoaderInterface(BaseLoaderInterface):

    def __init__(self, parent):
        super().__init__(parent)
        self.temp_sample_dir = pl.Path(tempfile.gettempdir()) / "SampleDataPyView"
        self.temp_sample_dir.mkdir(exist_ok=True)

    def load_list_fake(self):
        self.load_list([str(self.temp_sample_dir / "Fake")])

    def refresh_layout(self, widget):

        clear_layout(widget.layout())
        button = QPushButton("&Load sample Data", widget)
        button.clicked.connect(self.load_list_fake)
        widget.layout().addWidget(button)


class VIEWTIFFLoaderInterface(BaseLoaderInterface):

    def __init__(self, parent):

        super().__init__(parent)

    def refresh_layout(self, widget):
        clear_layout(widget.layout())

        view_tif_combobox = get_a_tiff_combobox(widget)
        view_tif_combobox.return_filenames_signal.connect(self.load_list)

        widget.layout().addWidget(view_tif_combobox)


class TillSingleLoaderInterface(BaseLoaderInterface):

    def __init__(self, parent):

        super().__init__(parent)

    def refresh_layout(self, widget):
        clear_layout(widget.layout())

        pst_combobox = get_a_pst_combobox(widget, multiple_selection_allowed=True)
        pst_combobox.return_filenames_signal.connect(self.load_list)

        widget.layout().addWidget(pst_combobox)


class TillDualLoaderInterface(BaseLoaderInterface):

    def __init__(self, parent):

        super().__init__(parent)

    def refresh_layout(self, widget):

        clear_layout(widget.layout())

        self.pst_1_combobox = get_a_pst_combobox(widget, multiple_selection_allowed=False)
        # for the case when pst2 combobox is selected before pst1 combobox
        self.pst_1_combobox.return_filename_signal.connect(self.load_list)

        widget.layout().addWidget(self.pst_1_combobox)

        self.pst_2_combobox = get_a_pst_combobox(widget, multiple_selection_allowed=False)
        # for the case when pst1 combobox is selected before pst2 combobox
        self.pst_2_combobox.return_filename_signal.connect(self.load_list)

        widget.layout().addWidget(self.pst_2_combobox)

    @pyqtSlot(str)
    def check_revise_filenames(self, filenames):

        dbb1_filename = self.pst_1_combobox.get_current_entry()
        dbb2_filename = self.pst_2_combobox.get_current_entry()

        # load data and return only if both dbb1 and dbb2 files have been selected
        if dbb1_filename is not None and dbb2_filename is not None:

            return [[dbb1_filename, dbb2_filename]]
        else:
            return None


class LifSingleLoaderInterface(BaseLoaderInterface):

    def __init__(self, parent):

        super().__init__(parent)

    def refresh_layout(self, widget):

        clear_layout(widget.layout())

        lif_combobox = get_a_lif_combobox(widget)
        lif_combobox.return_filenames_signal.connect(self.load_list)

        widget.layout().addWidget(lif_combobox)


class ZeissSingleLoaderInterface(BaseLoaderInterface):

    def __init__(self, parent):

        super().__init__(parent)

    def refresh_layout(self, widget):

        clear_layout(widget.layout())

        lsm_combobox = get_an_lsm_combobox(widget)
        lsm_combobox.return_filenames_signal.connect(self.load_list)

        widget.layout().addWidget(lsm_combobox)


def get_loader_interface_class(LE_loadExp):

    if LE_loadExp == 3:
        return TillSingleLoaderInterface
    elif LE_loadExp == 4:
        return TillDualLoaderInterface
    elif LE_loadExp == 20:
        return ZeissSingleLoaderInterface
    elif LE_loadExp == 21:
        return LifSingleLoaderInterface
    elif LE_loadExp == 33:
        return VIEWTIFFLoaderInterface
    elif LE_loadExp == 35:
        return VIEWTIFFLoaderInterface
    elif LE_loadExp in (665, 667, 676):
        return SampleData666LoaderInterface
    else:
        raise NotImplementedError

