# -*- coding: utf-8 -*-
"""
/***************************************************************************
 PluginGeometryDialog
                             -------------------
        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 (
    QgsVectorLayer,
)
from qgis.PyQt import uic
from qgis.PyQt.QtWidgets import (
    QDialog,
    QMessageBox,
    QRadioButton,
)

from . import LOGGER_NAME
from .ext.ndff.connector.connector import (
    NdffConnector,
)
from .ext.ndff.utils import (
    ellipsize2string,
)

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_geometry_dialog_base.ui")
)


class PluginGeometryDialog(QDialog, FORM_CLASS):
    """
    Dialog in which a user can choose how the Geometry / Location of this dataset will be constructed.

    The user can either use the QgsGeometry (in case we are using a true QGIS layer).
    Or can the plugin create a location from:
    - the (float) values from 2 fields (x and y)
    - an (E)WKT string value from a field in current Dataset

    A 'preview' is shown from current record

    The UI is actually a QStackedWidget in which the selection of one of the radio buttons shows/hides the different
    pages in it.

    The class is actually changing the mappings via the NdffConnector instance (referenced by via the 'show_dialog')
    """

    # Title to be used for all plugin dialogs (if possible)
    PLUGIN_DIALOGS_TITLE = "NDFF Connector Plugin"

    def __init__(self, parent=None):
        """
        When the user chooses to see/change the location(s) field(s), it pops up this dialog

        See show_dialog for more information.

        The active radio-button upon 'accept' actually SETS the choice (and unsets the others) in the
        NdffConnector (mappings) files

        """
        super(PluginGeometryDialog, self).__init__(parent)

        self.setupUi(self)
        self.radio_qgis_geom.toggled.connect(
            lambda: self.location_page_changed(self.radio_qgis_geom)
        )
        self.radio_x_y_geom.toggled.connect(
            lambda: self.location_page_changed(self.radio_x_y_geom)
        )
        self.radio_ewkt_geom.toggled.connect(
            lambda: self.location_page_changed(self.radio_ewkt_geom)
        )

        self.combo_location_ewkt.fieldChanged.connect(self.location_ewkt_field_set)
        self.combo_location_x.fieldChanged.connect(self.location_x_set)
        self.combo_location_y.fieldChanged.connect(self.location_y_set)

        self.record = None
        self.connector = None

    def show_dialog(
        self, connector: NdffConnector, record: dict, layer: QgsVectorLayer
    ):
        """
        There are 3 options:
        - the user chooses a wkt column, and set the 'location' mapping to that field (and unset location_x and location_y)
        - the user chooses 2 x,y columns and set 'location_x' and 'location_y' to those fields (and unset location)
        - the user chooses to use the QGIS column (IF available) then the location^* fields SHOULD ALL BE UNSET!!
        Within a QGIS layer all three can potentially be available.

        So when the dialog shows up, we:
        - check the field_mappings: order is preference
            - location_x and location_y set -> make those checked and show it
            - location set? -> make location radio checked and show it
            - nothing set and QGIS/geom layer -> make qgis checked and show it
            - nothing set and table layer -> disable qgis, default to xy and show nothing
        When in the dialog the user should be able to set/see all options, but:

        The active radio-button upon 'accept' actually SETS the choice (and unsets the others)

        """
        self.connector = connector
        self.record = record

        self.combo_location_x.setLayer(layer)
        self.combo_location_y.setLayer(layer)
        self.combo_location_ewkt.setLayer(layer)

        # cleanup of combos
        self.combo_location_x.setField("")
        self.combo_location_y.setField("")
        self.combo_location_ewkt.setField("")
        # cleanup of values
        self.example_location_x.setText("")
        self.example_location_y.setText("")
        self.example_location.setText("")
        self.example_location_qgis.setText("")

        # we added a field 'geometry' to the record IF the original feature (in QGIS)
        #   actually HAS a geometry (that is: was not a table-only layer)
        if "geometry" in record and record["geometry"]:
            self.radio_qgis_geom.setEnabled(True)
        else:
            # we are re-using the dialog, so be sure to unset it too
            self.radio_qgis_geom.setEnabled(False)

        # check if here are actually mappings, and if so
        # make that checked and show it:
        if connector.get_field_mapping("location_x") or connector.get_field_mapping(
            "location_y"
        ):
            log.debug("GEOM: we have a 'location_x' AND 'location_y' mapped...")
            self.radio_x_y_geom.setChecked(True)
            self.combo_location_x.setField(
                self.connector.get_field_mapping("location_x")
            )
            self.combo_location_y.setField(
                self.connector.get_field_mapping("location_y")
            )
        elif connector.get_field_mapping("location") or "location" in record:
            log.debug("GEOM: we have a mapping for 'location...")
            self.radio_ewkt_geom.setChecked(True)
            self.combo_location_ewkt.setField(
                self.connector.get_field_mapping("location")
            )
        elif "geometry" in record and record["geometry"]:
            # try to use the QGIS geom from this record
            # Toggle to force the showing of the geom
            self.radio_ewkt_geom.setChecked(True)
            self.location_page_changed(self.radio_x_y_geom)
            self.radio_qgis_geom.setChecked(True)
        else:  # no location fields set in mappings, AND NO geometry in data
            self.radio_x_y_geom.setChecked(True)
        self.show()

    def accept(self) -> None:
        """
        Qt api call to accept current state

        This actually will change (SET) the location mapping via the NdffConnector
        """
        # NOW we have to actually SET the location field mapping IF set, based on current active radio
        if self.radio_qgis_geom.isChecked():
            # remove the potentially available location, location_x and location_y field mappings
            self.connector.set_field_mapping("location", "")
            self.connector.set_field_mapping("location_x", "")
            self.connector.set_field_mapping("location_y", "")
            # nothing to set here... the plugin will try to use the QgisGeometry IF all location fields are Unset
        elif self.radio_x_y_geom.isChecked():
            # remove the potentially available location field mapping
            self.connector.set_field_mapping("location", "")
            # set the location_x and location_y mapping
            # BUT they should both have a (valid?) value
            if self.combo_location_x.currentField() in (
                "",
                None,
            ) or self.combo_location_y.currentField() in ("", None):
                QMessageBox.information(
                    self,
                    self.PLUGIN_DIALOGS_TITLE,
                    "BEIDE velden moeten worden gezet: x EN y ...",
                )
                return
            try:
                float(self.record[self.combo_location_x.currentField()])
            except ValueError:
                QMessageBox.information(
                    self,
                    self.PLUGIN_DIALOGS_TITLE,
                    f'De waarde voor het x veld: "{self.combo_location_x.currentField()}", lijkt geen geldig getal: "{self.record[self.combo_location_x.currentField()]}"',
                )
                return
            try:
                float(self.record[self.combo_location_y.currentField()])
            except ValueError:
                QMessageBox.information(
                    self,
                    self.PLUGIN_DIALOGS_TITLE,
                    f'De waarde voor het y veld: "{self.combo_location_y.currentField()}", lijkt geen geldig getal: "{self.record[self.combo_location_y.currentField()]}"',
                )
                return
            self.connector.set_field_mapping(
                "location_x", self.combo_location_x.currentField()
            )
            self.connector.set_field_mapping(
                "location_y", self.combo_location_y.currentField()
            )
        elif self.radio_ewkt_geom.isChecked():
            # remove the potentially available location_x and location_y field mappings
            self.connector.set_field_mapping("location_x", "")
            self.connector.set_field_mapping("location_y", "")
            # set the location mapping
            # BUT should valid WKT or EWKT
            if len(
                self.combo_location_ewkt.currentField().strip()
            ) > 0 and self.connector.is_valid_wkt(
                str(self.record[self.combo_location_ewkt.currentField()])
            ):
                self.connector.set_field_mapping(
                    "location", self.combo_location_ewkt.currentField()
                )
            else:
                # other than single point (e)wkt not yet implemented?
                QMessageBox.information(
                    self,
                    self.PLUGIN_DIALOGS_TITLE,
                    f'De waarde voor het wkt veld: "{self.combo_location_ewkt.currentField()}", lijkt geen geldig WKT point\nAndere waarden worden (nog) niet geaccepteerd...',
                )
                return
        self.done(QDialog.DialogCode.Accepted)

    def location_page_changed(self, radio_geom: QRadioButton):
        """
        Change the 'location'-page, the page in which to set the (type) of
        location: from a QgsGeometry, from an X and Y, from an (E)WKT-string
        """
        if radio_geom.objectName() == "radio_qgis_geom" and radio_geom.isChecked():
            self.stack_location.setCurrentIndex(0)
            self.example_location_qgis.setText(self.record["geometry"].asWkt())
        elif radio_geom.objectName() == "radio_x_y_geom" and radio_geom.isChecked():
            self.stack_location.setCurrentIndex(1)
        elif radio_geom.objectName() == "radio_ewkt_geom" and radio_geom.isChecked():
            self.stack_location.setCurrentIndex(2)

    def location_ewkt_field_set(self):
        """
        Will show the example/preview in case the user selected a (E)WKT field to use
        """
        log.debug(f"location_field_SET to {self.combo_location_ewkt.currentField()}")
        self.show_field_value(
            "location", self.combo_location_ewkt.currentField(), self.example_location
        )

    def location_x_set(self):
        """
        Will show the example/preview in case the user selected a field to use for x
        """
        log.debug(f"location_x_field_SET to {self.combo_location_x.currentField()}")
        self.show_field_value(
            "location_x", self.combo_location_x.currentField(), self.example_location_x
        )

    def location_y_set(self):
        """
        Will show the example/preview in case the user selected a field to use for y
        """
        log.debug(f"location_y_field_SET to {self.combo_location_y.currentField()}")
        self.show_field_value(
            "location_y", self.combo_location_y.currentField(), self.example_location_y
        )

    def show_field_value(self, field, mapped_to, button_to_show_on):
        """
        Show a value representation for current record for given location field option
        """
        max_len = 100
        if self.record and mapped_to in self.record:
            # we have mappings, data and the data contains the mapping
            button_to_show_on.setText(ellipsize2string(self.record[mapped_to], max_len))
        elif self.record and self.connector.get_field_default(field):
            button_to_show_on.setText(
                ellipsize2string(self.connector.get_field_default(field), max_len)
            )
        else:
            button_to_show_on.setText("-")
