# -*- coding: utf-8 -*-
"""
/***************************************************************************
 PluginDatasetCreateDialog
 This plugin connects to the NDFF api
                              -------------------
        begin                : 2021-12-20
        git sha              : $Format:%H$
        copyright            : (C) 2021 by Zuidt
        email                : richard@zuidt.nl
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

import logging
import os

from qgis._core import QgsFeature
from qgis.core import (
    QgsMapLayerProxyModel,
)
from qgis.PyQt import uic
from qgis.PyQt.QtCore import (
    Qt,
)
from qgis.PyQt.QtGui import (
    QStandardItem,
    QStandardItemModel,
)
from qgis.PyQt.QtWidgets import (
    QAbstractItemView,
    QDialog,
    QHeaderView,
    QMessageBox,
    QTableView,
)

from . import LOGGER_NAME
from .ext.ndff.api.object import (
    NdffDataset,
    NdffObservation,
)
from .ext.ndff.connector.connector import (
    NdffConnector,
)
from .ndffc_plugin_search_code_dialog import PluginSearchCodeDialog

log = logging.getLogger(LOGGER_NAME)

# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(
    os.path.join(
        os.path.dirname(__file__), "ndffc_plugin_dataset_create_dialog_base.ui"
    )
)

PLUGIN_DIALOGS_TITLE = "NDFF Connector Plugin"


class PluginDatasetCreateDialog(QDialog, FORM_CLASS):
    """
    This class is a simple Dialog to give the details of a (NDFF) Dataset.

    It's called from the 'Dataset (chooser) dialog' which holds all logic for the creation of the actual Dataset,
    and retrieves the details from this PluginDatasetCreateDialog instance.
    """

    def __init__(self, parent=None):
        """
        Constructor (setting up the UI/dialog based on the .ui file)
        """
        super(PluginDatasetCreateDialog, self).__init__(parent)
        self.setupUi(self)
        self.cb_dataset_layer.setFilters(
            QgsMapLayerProxyModel.Filter.PointLayer
            | QgsMapLayerProxyModel.Filter.LineLayer
            | QgsMapLayerProxyModel.Filter.PolygonLayer
        )
        self.cb_dataset_layer.setAllowEmptyLayer(
            True, text="Kies een QGIS data/kaartlaag met 1(!) (geselecteerde) geometrie"
        )
        self.cb_dataset_layer.setCurrentIndex(0)
        self.cb_dataset_layer.layerChanged.connect(self.dataset_layer_changed)
        self.location = None
        # doelsoort
        self.btn_add_doelsoort.clicked.connect(self.add_doelsoort)
        self.search_doelsoort_code_dlg = PluginSearchCodeDialog(parent=self)
        self.search_doelsoort_code_dlg.setModal(True)
        self.search_doelsoort_code_dlg.setWindowModality(
            Qt.WindowModality.ApplicationModal
        )  # https://doc.qt.io/qt-5/qt.html#WindowModality-enum
        self.search_doelsoort_code_dlg.finished.connect(self.add_doelsoort_finished)
        # doelsoorten from protocol
        self.btn_add_doelsoorten_from_protocol.clicked.connect(
            self.add_doelsoort_from_protocol
        )
        self.search_doelsoort_from_protocol_code_dlg = PluginSearchCodeDialog(
            parent=self
        )
        self.search_doelsoort_from_protocol_code_dlg.setModal(True)
        self.search_doelsoort_from_protocol_code_dlg.setWindowModality(
            Qt.WindowModality.ApplicationModal
        )  # https://doc.qt.io/qt-5/qt.html#WindowModality-enum
        self.search_doelsoort_from_protocol_code_dlg.finished.connect(
            self.add_doelsoort_from_protocol_finished
        )
        # remove doelsoort
        self.btn_remove_doelsoort.clicked.connect(self.remove_doelsoort)

        self.doelsoorten_model = QStandardItemModel()
        self.tbl_doelsoorten.setModel(self.doelsoorten_model)
        # select full row upon clicking on a cell / row
        self.tbl_doelsoorten.setSelectionBehavior(
            QTableView.SelectionBehavior.SelectRows
        )
        # only one row selectable at a time
        self.tbl_doelsoorten.setSelectionMode(
            QAbstractItemView.SelectionMode.SingleSelection
        )
        # make table cells non-editable:
        self.tbl_doelsoorten.setEditTriggers(
            QAbstractItemView.EditTrigger.NoEditTriggers
        )
        self.tbl_doelsoorten.verticalHeader().setVisible(False)
        self.tbl_doelsoorten.horizontalHeader().setVisible(False)

    def add_doelsoort(self):
        """
        Open a search code dialog searching for 'vleermuis' in the taxa code list.
        """
        self.search_doelsoort_code_dlg.setup_gui(
            self.parent().connector,
            "taxon",
            search_value="vleermuis",  # actual value to search for
            abundance_schema_uri="",
        )
        self.search_doelsoort_code_dlg.open()

    def add_doelsoort_finished(self):
        """
        Based on the single selected row of the search code dialog with 'doelsoorten', add the uri of it
        to the dataset.
        """
        selected_data = self.search_doelsoort_code_dlg.selected_data
        self.add_doelsoort_to_set(selected_data)

    def add_doelsoort_from_protocol(self):
        """
        Open a search code dialog searching for 'snl' in the category/policy code list.
        """
        self.search_doelsoort_code_dlg.setup_gui(
            self.parent().connector,
            "category",
            search_value="snl",  # actual value to search for
            abundance_schema_uri="",
            search_field=None,
        )
        self.search_doelsoort_code_dlg.open()

    def add_doelsoort_from_protocol_finished(self):
        """
        Based on the single selected row of the search code dialog with 'doelsoort protocols', add the uri of it
        to the dataset.
        """
        selected_data = self.search_doelsoort_code_dlg.selected_data
        self.add_doelsoort_to_set(selected_data)

    def resize_doelsoorten_columns(self):
        """
        Resize the doelsoorten columns based on the contents of the table.
        """
        self.tbl_doelsoorten.horizontalHeader().setSectionResizeMode(
            0, QHeaderView.ResizeMode.Stretch
        )
        self.tbl_doelsoorten.horizontalHeader().setSectionResizeMode(
            1, QHeaderView.ResizeMode.ResizeToContents
        )
        self.tbl_doelsoorten.resizeColumnToContents(0)
        self.tbl_doelsoorten.resizeColumnToContents(1)

    def add_doelsoort_to_set(self, data: dict):
        """
        Given a dict with name and uri, do a check if the uri is already in the doelsoorten list in the model.
        """
        # first collect existing uri's
        existing_uris = []
        for row_number in range(0, self.doelsoorten_model.rowCount()):
            uri = self.doelsoorten_model.data(
                self.doelsoorten_model.index(row_number, 1).siblingAtColumn(1)
            )
            existing_uris.append(uri)

        if "name" in data and "uri" in data:
            if data["uri"] in existing_uris:
                QMessageBox.information(
                    self,
                    "Dataset",
                    f"Deze uri '{data["uri"]}' is al aanwezig en wordt genegeerd.",
                )
            else:
                self.doelsoorten_model.appendRow(
                    [
                        QStandardItem(data["name"]),
                        QStandardItem(data["uri"]),
                    ]
                )
        self.resize_doelsoorten_columns()

    def set_doelsoorten(self, doelsoorten: list):
        """
        Fill the doelsoorten_model table based on a list of uri's
        """
        for uri in doelsoorten:
            # try to fetch names of the doelsoorten or doelsoort protocols
            name = ""
            if "taxonomy" in uri:
                doelsoort = self.parent().connector.search_codes(search_text=uri)
                if len(doelsoort) > 0:
                    name = doelsoort[0]["name"]
            elif "categories" in uri:
                protocol = self.parent().connector.search_codes(
                    search_text=uri, search_field="category"
                )
                if len(protocol) > 0:
                    name = protocol[0]["name"]
            self.doelsoorten_model.appendRow(
                [
                    QStandardItem(name),
                    QStandardItem(uri),
                ]
            )
        self.resize_doelsoorten_columns()

    def get_doelsoorten(self) -> list:
        """
        Get the doelsoort or protocol-doelsoorten uri's from the doelsoorten_model table
        """
        doelsoorten = []
        for row_number in range(0, self.doelsoorten_model.rowCount()):
            uri = self.doelsoorten_model.data(
                self.doelsoorten_model.index(row_number, 1).siblingAtColumn(1)
            )
            doelsoorten.append(uri)
        return doelsoorten

    def remove_doelsoort(self):
        """
        This method will remove the selected model's row (containing a doelsoort uri or list) or give a message to
        the user to select a row.
        """
        selected_row_indexes = self.tbl_doelsoorten.selectionModel().selectedRows()
        if len(selected_row_indexes) == 0:
            QMessageBox.information(
                self,
                "Dataset",
                "Niets geselecteerd\nSelecteer eerst een rij uit de doelsoorten-tabel",
            )
        else:
            self.doelsoorten_model.removeRows(selected_row_indexes[0].row(), 1)

    def dataset_layer_changed(self, layer: QgsMapLayerProxyModel) -> None:
        """
        This method is called when the dataset layer changes in the dataset create dialog.

        It checks IF the layer that is selected, either
        - has just one Feature
        or
        - has just one Feature SELECTED

        """
        layer = self.cb_dataset_layer.currentLayer()
        feature = QgsFeature()
        if layer is None:
            # OK
            self.cb_dataset_layer.setCurrentIndex(0)
            return

        # the cb_dataset_layer has a filter for ONLY (vector) Line- or Polgyon-layers, so just check for features
        if layer.featureCount() == 1:
            layer.getFeatures().nextFeature(feature)
        elif layer.selectedFeatureCount() == 1:
            feature = layer.selectedFeatures()[0]
        else:
            QMessageBox.warning(
                self,
                PLUGIN_DIALOGS_TITLE,
                "De laag voor de Dataset/Map mag maar 1 Feature/Geometrie bevatten, \n"
                "OF\ner mag maar 1 Feature/Geometrie in de laag zijn geselecteerd!",
            )
            self.cb_dataset_layer.setCurrentIndex(0)
            return

        o = NdffObservation()  # silly placeholder to be able to use parse_wkt_wkb
        NdffConnector.parse_wkt_wkb(o, feature.geometry().asWkt())
        precision = 5
        if NdffConnector.get_crs(o) == NdffConnector.EPSG_28992:
            precision = 1
        NdffConnector.parse_wkt_wkb(
            o, feature.geometry().asWkt(precision=precision)
        )  # second round, to get the precision right...
        self.location = o.location
        if "olygon" in feature.geometry().asWkt():
            # (Multi)Polygons should have a buffer of 0, all others we set to 10
            self.location["buffer"] = 0
            # also check max area
            msg = (
                "De NDFF verwacht geen onderzoekslocaties groter dan 1.700 km².\n"
                "De QGIS-connector is niet bedoeld voor het verwerken van zulke grote datasets.\n\n"
                "Neem contact op met de NDFF om de mogelijkheden te bespreken."
            )
            if (
                NdffConnector.get_crs(o) == NdffConnector.EPSG_28992
                and feature.geometry().area() > 1700000000
            ):
                QMessageBox.warning(self, PLUGIN_DIALOGS_TITLE, msg)
            elif (
                NdffConnector.get_crs(o) == NdffConnector.EPSG_4326
                and feature.geometry().area() > 0.25
            ):
                QMessageBox.warning(self, PLUGIN_DIALOGS_TITLE, msg)
        else:
            # lines and points have buffer of 10
            self.location["buffer"] = 10

        tmp_dataset = (
            NdffDataset()
        )  # creating a dataset here, pure for proper visualisation
        tmp_dataset.set("location", self.location)
        self.lbl_current_location.setText(
            f"Locatie  -->  {tmp_dataset.get('location')}"
        )

        log.debug(f"Created dataset location: {self.location}")

    def accept(self) -> None:
        """
        Qt api call: called when the user clicks OK or other ways accepts the dialog
        """
        log.debug("OK clicked; DatasetCreate dialog")
        self.done(QDialog.DialogCode.Accepted)
