# -*- coding: utf-8 -*-
#
#   Copyright (C) 2019 Oslandia <infos@oslandia.com>
#
#   This file is a piece of free software; you can redistribute it and/or
#   modify it under the terms of the GNU Library General Public
#   License as published by the Free Software Foundation; either
#   version 2 of the License, or (at your option) any later version.
#
#   This library is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#   Library General Public License for more details.
#   You should have received a copy of the GNU Library General Public
#   License along with this library; if not, see <http://www.gnu.org/licenses/>.
#

from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtWidgets import QDialog, QVBoxLayout, QDialogButtonBox, QAbstractItemView
from qgis.PyQt.QtWidgets import (
    QListWidget,
    QListWidgetItem,
    QHBoxLayout,
    QLabel,
    QComboBox,
    QPushButton,
)

from qgis.core import QgsProject, QgsFeatureRequest

from .qgeologis.data_interface import LayerData, FeatureData, IntervalData

import json


class DataSelector(QDialog):
    def __init__(self, viewer, features, config_list, config, removeMode=False):
        """ DataSelector explore configuration to show log (to display or erase)

        :param viewer: WellLogViewWrapper or TimeSeriesWrapper
        :type viewer: WellLogViewWrapper or TimeSeriesWrapper
        :param features: selected features
        :type features: list
        :param config_list: list of log configuration to display
        :type config_list: list
        :param config: layer configuration
        :type config: dict
        :param removeMode: to display add or remove data selector
        :type removeMode: bool
        """
        QDialog.__init__(self)

        self.__viewer = viewer
        self.__features = features
        self.__config_list = config_list
        self.__config = config
        self.__removeMode = removeMode
        self.__config_removed = False
        vbox = QVBoxLayout()

        btn = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        btn.accepted.connect(self.accept)
        btn.rejected.connect(self.reject)

        self.__list = QListWidget()
        self.__list.setSelectionMode(QAbstractItemView.ExtendedSelection)

        hbox = QHBoxLayout()
        self.__lbl = QLabel(self.tr("Sub selection"))
        self.__sub_selection_combo = QComboBox()
        self.__sub_selection_combo.setEnabled(False)
        hbox.addWidget(self.__lbl)
        hbox.addWidget(self.__sub_selection_combo)

        self.__title_label = QLabel()

        hbox2 = QHBoxLayout()
        hbox2.addWidget(self.__title_label)

        vbox.addLayout(hbox2)
        vbox.addWidget(self.__list)
        vbox.addLayout(hbox)
        if self.__removeMode:
            self.__lbl.hide()
            self.__sub_selection_combo.hide()
            self.__lyrConfigRemoveButton = QPushButton(self.tr("Remove layer configuration"))
            self.__lyrConfigRemoveButton.clicked.connect(self.__remove_layer_configuration)
            vbox.addWidget(self.__lyrConfigRemoveButton)
        vbox.addWidget(btn)

        self.__list.itemSelectionChanged.connect(self.on_selection_changed)
        self.__sub_selection_combo.currentIndexChanged[str].connect(self.on_combo_changed)

        title = self.tr("Choose the data to add")
        if self.__removeMode:
            title = self.tr("Choose the data to remove")
        self.setLayout(vbox)
        self.setWindowTitle(title)
        self.resize(400, 200)

        self._populate_list()
        try:
            self.set_title(
                ",".join([feature[self.__config["name_column"]] for feature in self.__features])
            )
        except TypeError:
            self.__title_label.setText(
                self.tr("Unvalid configuration, please remove configuration")
            )

    def set_title(self, title):
        """ Set window title

        :param title: selected station names
        :type title: str list
        """
        self.__title_label.setText("Station(s): {}".format(title))

    def _populate_list(self):
        """ Populate ListWidget from __config_list attribute"""
        self.__list.clear()
        for cfg in self.__config_list:
            if cfg["type"] in ("cumulative", "instantaneous", "stratigraphic"):
                # check number of features for this station

                if (
                    cfg.get("feature_filter_type") == "unique_data_from_values"
                    and not self.__removeMode
                ):
                    # get unique filter values

                    layerid = cfg["source"]
                    data_l = QgsProject.instance().mapLayers()[layerid]
                    values = set()
                    for feature in self.__features:
                        feature_id = feature[self.__config["id_column"]]
                        req = QgsFeatureRequest()
                        req.setFilterExpression(
                            "{}={}".format(cfg["feature_ref_column"], feature_id)
                        )
                        values.update(
                            [f[cfg["feature_filter_column"]] for f in data_l.getFeatures(req)]
                        )

                        cfg.set_filter_unique_values(sorted(list(values)))

            item = QListWidgetItem(cfg["name"])
            item.setData(Qt.UserRole, cfg)
            if not cfg["source"] in [
                item.data(Qt.UserRole)["source"]
                for item in self.__list.findItems(cfg["name"], Qt.MatchExactly)
            ]:
                self.__list.addItem(item)

    def accept(self):
        """ Accept method, reload if log added """
        if not self.__removeMode:
            for feature in self.__features:
                self.__load_feature(feature)
        else:
            QDialog.accept(self)

    def items(self):
        """ Return selected items """
        if self.__sub_selection_combo.count() > 0 and len(self.__list.selectedItems()) > 1:
            print(self.tr("Categorical plot should be added separately"))
            return []
        else:
            return [item.data(Qt.UserRole) for item in self.__list.selectedItems()]

    def __load_feature(self, feature):
        """" Load logs from a selected feature

        :param feature: feature
        :type feature: QgsFeature
        """
        # FIXME this code too similar to main_dialog.load_plots
        # FIXME find a way to factor

        feature_id = feature[self.__config["id_column"]]
        feature_name = feature[self.__config["name_column"]]
        if self.__config["ground_elevation"]:
            feature_elevation = feature[self.__config["ground_elevation"]]
        else:
            feature_elevation = False

        for item in self.__list.selectedItems():
            # now add the selected configuration
            cfg = item.data(Qt.UserRole)
            if cfg["type"] in ("cumulative", "instantaneous", "continuous"):
                layerid = cfg["source"]
                data_l = QgsProject.instance().mapLayers()[layerid]
                req = QgsFeatureRequest()
                filter_expr = "{}='{}'".format(cfg["feature_ref_column"], feature_id)
                req.setFilterExpression(filter_expr)

                title = cfg["name"]

                if cfg.get_filter_value():
                    filter_expr += " and {}='{}'".format(
                        cfg["feature_filter_column"], cfg.get_filter_value()
                    )

                    title = cfg.get_filter_value()
                else:
                    title = cfg["name"]

                f = None
                # test if the layer actually contains data
                for f in data_l.getFeatures(req):
                    break
                if f is None:
                    print(self.tr("No feature found in layer") + " {}".format(cfg["name"]))
                    return QDialog.accept(self)
                if cfg["type"] == "instantaneous":
                    uom = cfg.get_uom()
                    data = LayerData(
                        data_l,
                        feature_elevation,
                        cfg["event_column"],
                        cfg["value_column"],
                        cfg["uncertainty_column"],
                        filter_expression=filter_expr,
                        uom=uom,
                    )
                    uom = data.get_uom()
                    self.__viewer.add_data_cell(data, title, station_name=feature_name, config=cfg)
                    scale_title = "Depth" if self.__viewer.orientation() else "Time"
                    self.__viewer.add_scale(title=scale_title)
                if cfg["type"] == "continuous":
                    uom = cfg.get_uom()
                    fids = [f.id() for f in data_l.getFeatures(req)]
                    data = FeatureData(
                        data_l,
                        feature_elevation,
                        cfg["values_column"],
                        feature_ids=fids,
                        uncertainty_fieldname=cfg["uncertainty_column"],
                        x_start_fieldname=cfg["start_measure_column"],
                        x_delta_fieldname=cfg["interval_column"],
                        uom=uom,
                    )
                    self.__viewer.add_data_cell(data, title, station_name=feature_name, config=cfg)
                elif cfg["type"] == "cumulative":
                    uom = cfg.get_uom()
                    # FIXME for next release: set default value to "remove"
                    nodata = cfg.get("nodata", 0.0)
                    if nodata == "remove":
                        nodata = None
                    data = IntervalData(
                        data_l,
                        feature_elevation,
                        [cfg["min_event_column"], cfg["max_event_column"]],
                        cfg["value_column"],
                        cfg["uncertainty_column"],
                        filter_expression=filter_expr,
                        uom=uom,
                        nodata_value=nodata,
                    )
                    self.__viewer.add_data_cell(data, title, station_name=feature_name, config=cfg)

            elif cfg["type"] == "image":
                layer_name = cfg["name"]
                layer = QgsProject.instance().mapLayersByName(layer_name)[0]
                self.__viewer.add_imagery_from_db(
                    cfg, feature_id, layer, feature_elevation, feature_name
                )

        QDialog.accept(self)

    def on_selection_changed(self):
        """ Triggered on selection change """
        self.__sub_selection_combo.clear()
        self.__sub_selection_combo.setEnabled(False)
        for item in self.__list.selectedItems():
            cfg = item.data(Qt.UserRole)
            for v in cfg.get_filter_unique_values():
                self.__sub_selection_combo.addItem(v)
            if cfg.get_filter_value():
                self.__sub_selection_combo.setCurrentIndex(
                    self.__sub_selection_combo.findText(cfg.get_filter_value())
                )
            self.__sub_selection_combo.setEnabled(True)
            return

    def on_combo_changed(self, text):
        """ Triggered on combo change, for sub item

        :param text: text
        :param text: str
        """
        for item in self.__list.selectedItems():
            cfg = item.data(Qt.UserRole)
            cfg.set_filter_value(text)
            item.setData(Qt.UserRole, cfg)
            return

    def get_config_removed(self):
        """ In remove mode, return __config_removed attribute
        if the configuration has been removed, return True

        :return: remove configuration attribute
        :rtype: bool
        """
        return self.__config_removed

    def __remove_layer_configuration(self):
        """ Triggered on remove layer configuration button """
        self.__list.clear()
        config, _ = QgsProject.instance().readEntry("QGeoloGIS", "config", "{}")
        config = json.loads(config)
        if self.__config.get_layer_id() in config.keys():
            del config[self.__config.get_layer_id()]
        QgsProject.instance().writeEntry("QGeoloGIS", "config", json.dumps(config))
        self.__config_removed = True
        self.accept()
