# -*- coding: utf-8 -*-
"""
/***************************************************************************
 Silextracteur
                                 A QGIS plugin
 Plugin pour l'extraction de formations géologiques à silicites depuis les flux WFS du GDR SILEX
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                             -------------------
        begin              : 2024-06-03
        git sha            : $Format:%H$
        copyright          : (C) 2024 by Thomas ANDRE
        email              : thomas.andre.archgeo@gmail.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 os

from qgis import *
from qgis.PyQt import *
from qgis.core import *
from qgis.utils import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from processing.tools import dataobjects
import processing
from .Fonctions.Utilitaires import *
from .Fonctions.Fonctions_QGIS import *


# 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__), 'Silextracteur_dialog_base.ui'))


class SilextracteurDialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, parent=None):
        """Constructor."""
        super(SilextracteurDialog, self).__init__(parent)
        # Set up the user interface from Designer through FORM_CLASS.
        # After self.setupUi() you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)

        # Initialisation of some functions and tools
        self.layers = QgsProject.instance().mapLayers().values()
        self.attribute = "etage_final"
        self.combo_zone.setFilters(QgsMapLayerProxyModel.PolygonLayer)
        self.select_admin.setFilters(QgsMapLayerProxyModel.PolygonLayer)
        self.combo_cities.setFilters(QgsMapLayerProxyModel.PointLayer)
        self.charge = False

        # Initialisation of the loading bar
        message = "Avancement dans la récupération des formations géologiques"
        percent = 0
        Utilitaires.progression(self.bar_1, self.progres_label, message, percent)
        self.bar_1.setValue(percent)

        # Protection of the buttons to prevent misuses
        self.combo_feature.setEnabled(False)
        self.name_zone.setEnabled(False)
        self.ok_zone_2.setEnabled(False)
        self.combo_admin.setEnabled(False)
        self.ok_admin.setEnabled(False)
        self.select_admin.setEnabled(False)
        self.ok_zone_3.setEnabled(False)
        self.launch.setEnabled(False)
        self.name_admin.setEnabled(False)
        self.combo_wfs.setEnabled(False)
        self.ok_wfs.setEnabled(False)
        self.choice_1.setChecked(True)
        self.list_strati.setEnabled(False)
        self.ok_strati.setEnabled(False)
        self.combo_cities.setEnabled(False)
        self.ok_cities.setEnabled(False)
        self.attribute_cities.setEnabled(False)
        self.name_cities.setEnabled(False)
        self.ok_cities_2.setEnabled(False)
        self.check_cities.setEnabled(False)
        self.half_launch.setEnabled(False)
        self.final_launch.setEnabled(False)

        self.select_admin.hide()
        self.ok_zone_3.hide()
        self.name_admin.hide()
        self.launch.hide()
        self.label_8.hide()
        self.label_15.hide()

        # Linking buttons and functions
        self.preset.clicked.connect(Utilitaires.add_layers)
        self.ok_zone.clicked.connect(self.load_zone)
        self.combo_feature.fieldChanged.connect(self.select_attribute)
        self.ok_zone_2.clicked.connect(self.select_zone)
        self.ok_admin.clicked.connect(self.select_adm)
        self.ok_zone_3.clicked.connect(self.select_divi)
        self.launch.clicked.connect(self.load_wfs)
        self.ok_wfs.clicked.connect(self.select_wfs)
        self.choice_2.toggled.connect(self.types_list)
        self.choice_1.toggled.connect(self.types_list)
        self.ok_strati.clicked.connect(self.select_geol)
        self.check_cities.toggled.connect(self.access_cities)
        self.ok_cities.clicked.connect(self.load_cities)
        self.attribute_cities.fieldChanged.connect(self.select_attr_cities)
        self.ok_cities_2.clicked.connect(self.select_cities)
        self.half_launch.clicked.connect(self.select_strati)
        self.final_launch.clicked.connect(self.final_layer)


    def access_cities(self):
        """
        Function: Modification of information and access to the module depending on whether the user wishes to use a city or not
        """
        # Verification that users wish to use a town for the selection process
        if self.check_cities.isChecked():
            self.combo_cities.setEnabled(True)
            self.ok_cities.setEnabled(True)
            self.half_launch.setEnabled(False)

            message = 'Sélectionner la couche des communes (6/8)'
            self.progres_label.setText(message)

        else:
            self.combo_cities.setEnabled(False)
            self.ok_cities.setEnabled(False)
            self.attribute_cities.setEnabled(False)
            self.name_cities.setEnabled(False)
            self.ok_cities_2.setEnabled(False)
            self.half_launch.setEnabled(True)

            selected_features = len(self.GDR_SILEX.selectedFeatures())
            message = "Formation géologique sélectionnée (Nb selected_features: {}) (6/6)".format(selected_features)
            self.progres_label.setText(message)


    def types_list(self):
        """
        Function: Allows to change the field listed in the drop-down list when selecting the stratigraphy
        """
        try:
            if self.choice_1.isChecked():
                self.choice_2.setChecked(False)
                self.attribute = "etage_final"
                self.select_wfs()

            elif self.choice_2.isChecked():
                self.choice_1.setChecked(False)
                self.attribute = "descr"
                self.select_wfs()
        except:
            pass


    def load_zone(self):
        """
        Function: Enable selection of extraction zones once the layer has been selected
        """
        try:
            self.zone_layer = Utilitaires.layer_finder(self.combo_zone)
            # Define the source of the drop-down list of features in the selected layer
            self.combo_feature.setLayer(self.zone_layer)
            self.name_zone.setSourceLayer(self.zone_layer)

            self.combo_feature.setEnabled(True)
            self.name_zone.setEnabled(True)
            self.ok_zone_2.setEnabled(True)

            message = "Sélectionnez la zone d'extraction recherchée (1/6)"
            self.progres_label.setText(message)
        except:
            message = "La couche sélectionnée ne contient aucun champ.. Veuillez changer de couche"
            self.progres_label.setText(message)

    
    def select_attribute(self):
        """
        Function: Selecting an attribute field
        """
        self.attribute_zone = self.combo_feature.currentField()
        # print(str(self.attribute_zone)) # debugtest
        # Change of reference field for entity selection
        self.name_zone.setDisplayExpression(self.attribute_zone)


    def select_zone(self):
        """
        Function: Select the desired extraction zone
        """
        try:
            self.attribute_zone = self.combo_feature.currentField()
            zone_name = self.name_zone.currentText()
            # print(str(zone_name)) # debugtest
            # Selection in the layer using a given expression
            Fonctions_QGIS.Selectbyattribute(self, self.zone_layer, self.attribute_zone, zone_name, 0)
            if self.charge:
                if self.combo_admin.currentText () == "Départements français":
                    self.select_admin.hide()
                    self.ok_zone_3.hide()
                    self.name_admin.hide()
                    self.launch.hide()
                    self.label_8.hide()
                    self.label_15.hide()

                    if not QgsProject.instance().mapLayersByName("2 - Departements"):
                        self.admin_layer = QgsProject.instance().mapLayersByName("Departements")[0]
                    else:
                        self.admin_layer = QgsProject.instance().mapLayersByName("2 - Departements")[0]

                else:
                    self.admin_layer = Utilitaires.layer_finder(self.select_admin)
                self.load_wfs()
                message = "Zone d'extraction sélectionnée (2/6)"
                self.progres_label.setText(message)

            self.combo_admin.setEnabled(True)
            self.ok_admin.setEnabled(True)
        except:
            message = "L'entité choisie n'a pas été trouvée dans la couche. Veuillez en choisir une autre"
            self.progres_label.setText(message)
        


    def select_adm(self):
        """
        Function: Choice of administrative boundary layer
        """
        try:
            QgsProject.instance().mapLayersByName(self.admin_layer.name())
            self.charge = False
        except:
            self.charge = True

        if self.combo_admin.currentText () == "Départements français":
            self.select_admin.hide()
            self.ok_zone_3.hide()
            self.name_admin.hide()
            self.launch.hide()
            self.label_8.hide()
            self.label_15.hide()

            if not QgsProject.instance().mapLayersByName("2 - Departements"):
                # Access to the layer in the module folder
                path = os.path.dirname(os.path.abspath(__file__))
                path = path + '//Couches//{}.shp'.format("Departements")
                layer = QgsVectorLayer(path, "Departements", "ogr")

                # Defining the layer style
                style_path = os.path.dirname(os.path.abspath(__file__))
                style_path = style_path + '//Styles//{}.qml'.format("Departements")
                layer.loadNamedStyle(style_path)

                if not QgsProject.instance().mapLayersByName("Departements"):
                    # Adding the layer to the map
                    QgsProject.instance().addMapLayer(layer)
                self.admin_layer = QgsProject.instance().mapLayersByName("Departements")[0]

            else:
                self.admin_layer = QgsProject.instance().mapLayersByName("2 - Departements")[0]
            self.column_admin = "nom"
            self.load_wfs()

        elif self.combo_admin.currentText () == "Personalisée":
            self.select_admin.show()
            self.ok_zone_3.show()
            self.name_admin.show()
            self.launch.show()
            self.label_8.show()
            self.label_15.show()
            self.select_admin.setEnabled(True)
            self.ok_zone_3.setEnabled(True)


    def select_divi(self):
        """
        Function: Selecting the layer containing administrative entities
        """
        try:
            self.admin_layer = Utilitaires.layer_finder(self.select_admin)
            # Define the source of the drop-down list of features in the selected layer
            self.name_admin.setLayer(self.admin_layer)

            self.name_admin.setEnabled(True)
            self.launch.setEnabled(True)

            message = "Sélectionnez la colonne de nom des entités administratives (3/6)"
            self.progres_label.setText(message)
        except:
            message = "La couche choisie n'a pas été trouvée, ou ne possède pas de champs utilisables"
            self.progres_label.setText(message)


    def load_wfs(self):
        """
        Function: Loading of the GDR SILEX WFS flow covering the whole of France, list of administrative entities concerned and recovery of an initial sample of geological formation descriptions
        """
        try:
            if self.combo_admin.currentText () == "Personalisée":
                # Retrieve the column header from the administrative entities layer containing their Name
                self.column_admin = self.name_admin.currentField()
        except:
            message = "La couche de limites administratives ne possède pas le champ sélectionné"
            self.progres_label.setText(message)
            return 
        
        # Creation of an erosion of the extraction zone to select the administrative entities it covers, removing problems of precision/simplification of boundaries
        buffered_layer = Fonctions_QGIS.Buffer(self, self.zone_layer, -2500, True, True)
        if self.column_admin == '':
            self.charge = False

        message = "Chargement du Flux WFS... (Cette étape peut prendre quelques instants la première fois)"
        percent = 50
        Utilitaires.progression(self.bar_1, self.progres_label, message, percent)

        url = 'https://services3.arcgis.com/zy1UGXGNCAGm516O/arcgis/rest/services/Formations_France_25032025/FeatureServer/0'
        bool = True

        layers = QgsProject.instance().mapLayers().values()
        for layer in layers:
            # print(layer.source()) # debutest
            if url in layer.source():
                bool = False
                self.GDR_SILEX = layer

        # Check that one of the layers is not already loaded
        if bool:
            try:
                # Preparing the loading parameters for the WFS stream
                uri = QgsDataSourceUri()
                # print(url) # debudtest
                uri.setParam('url', url)
                uri.setSrid('EPSG:3857')
                self.GDR_SILEX = QgsVectorLayer(uri.uri(), "WFS_GDR_SILEX" , 'arcgisfeatureserver')

                # Adding the layer to the map
                QgsSettings().setValue("/qgis/wfs/useCachedFeatureDownload", True)
                iface.mapCanvas().setRenderFlag(False)
                QgsProject.instance().addMapLayer(self.GDR_SILEX)

                # Creation of a spatial index to facilitate searches by location
                self.GDR_SILEX.dataProvider().createSpatialIndex()
                style_path = os.path.dirname(os.path.abspath(__file__)) + '//Styles//Legende_CODE_HEXA_ETAGE_FINAL_vEV.qml'

                if os.path.exists(style_path):
                    self.GDR_SILEX.loadNamedStyle(style_path)
                # Do not display layer entities to limit calculations
                QgsProject.instance().layerTreeRoot().findLayer(self.GDR_SILEX.id()).setItemVisibilityChecked(False)
                iface.mapCanvas().setRenderFlag(True)
                self.charge = True
            except:
                message = "Impossible de charger le flux WFS... Veuillez vérifier votre connexion internet"
                self.progres_label.setText(message)
                iface.messageBar().pushMessage("Erreur:", message, level=2)
                return
        else:
            self.charge = True

        # Deselection of entities to facilitate searches and avoid duplication
        self.admin_layer.removeSelection()

        # Selection by location of administrative entities covered by erosion
        Fonctions_QGIS.Selectbylocation_simple(self, self.admin_layer, buffered_layer, 0)
        # Selection by location of geological formations intersecting the selected administrative units
        Fonctions_QGIS.Selectbylocation(self, self.GDR_SILEX, self.admin_layer, True, 0)

        if len(self.GDR_SILEX.selectedFeatures()) != 0:
            # Creation of a list of administrative entities
            self.combo_wfs.clear()
            self.list_depart = []
            # Looping selected elements in the layer
            for feature in self.admin_layer.selectedFeatures():
                # print(feature['{}'.format(self.column_admin)]) # debudtest
                name = feature['{}'.format(self.column_admin)]
                # print(name) # debudtest
                self.list_depart.append(name)
            # Sorting the list
            self.list_depart.sort()
            # Adding administrative entities to the drop-down list
            self.combo_wfs.addItems(self.list_depart)

            # Creating a list of descriptions
            self.list_strati.clear()
            descr_list = []
            # Resetting the drop-down list for new selections
            self.list_strati.clear()
            self.list_strati.clearEditText()
            # Looping selected elements in the layer
            for feature in self.GDR_SILEX.selectedFeatures():
                descr_list.append(feature[self.attribute])
            # Retrieving unique values
            unique = set(descr_list)
            descr_list = list(unique)
            # Sorting the list
            descr_list.sort()
            # Adding descriptions to the drop-down list
            self.list_strati.addItems(descr_list)

            self.combo_wfs.setEnabled(True)
            self.ok_wfs.setEnabled(True)
            self.list_strati.setEnabled(True)
            self.ok_strati.setEnabled(True)

            self.bar_1.reset()
            selected_features = len(self.GDR_SILEX.selectedFeatures())
            message = "Préparations terminées (Nb selected_features: {}) (4/6)".format(selected_features)
            self.progres_label.setText(message)

        else:
            self.bar_1.reset()
            message = "Aucune formation sélectionnable"
            self.progres_label.setText(message)


    def select_wfs(self):
        """
        Function: Choice of administrative entities and selection of related geological layers
        """
        message = "Récupération des formations géologiques de l'entité administrative..."
        percent = 50
        Utilitaires.progression(self.bar_1, self.progres_label, message, percent)

        admin = self.combo_wfs.currentText()
        # print("A: " + admin) # debugtest

        # Selection by administrative entity attribute selected in the administrative entities layer 
        Fonctions_QGIS.Selectbyattribute(self, self.admin_layer, self.column_admin,admin, 0)
        # print("B: "+str(len(self.admin_layer.selectedFeatures()))) # debugtest
        # Selection by location of geological formations covered by the selected administrative unit
        Fonctions_QGIS.Selectbylocation(self, self.GDR_SILEX, self.admin_layer, True, 0)
        
        # Creation of a list of descriptions
        descr_list = []
        # Resetting the drop-down list for new selections
        self.list_strati.clear()
        self.list_strati.clearEditText()
        # print("C: "+str(len(self.GDR_SILEX.selectedFeatures()))) # debugtest

        # Looping selected elements in the layer
        for feature in self.GDR_SILEX.selectedFeatures():
            # Creation of a list to contain the Name of the administrative entity and its version in upper case
            list_admin = []
            d = admin
            list_admin.append(d)
            # Mise en majuscule
            D = d.upper()
            list_admin.append(D)
            # print(list_admin) # debugtest
            # print(str(feature["nom_dept"])) # debugtest
            # print(str(feature["nom_dept"]) == list_admin) # debugtest

            # Only add descriptions of geological formations if they are linked to the administrative unit selected once.
            if feature[self.attribute] not in descr_list and feature["nom_dept"] in list_admin:
                descr_list.append(feature[self.attribute])

        # Sorting the list
        self.list_strati.clear()
        descr_list.sort()
        # Adding descriptions to the drop-down list
        self.list_strati.addItems(descr_list)
        # Deselection of entities to facilitate searches and avoid duplication
        self.admin_layer.removeSelection()

        self.bar_1.reset()
        selected_features = len(self.GDR_SILEX.selectedFeatures())
        message = "Sélectionnez la formation géologique recherchée (Nb selected_features: {}) (5/6)".format(selected_features)
        self.progres_label.setText(message)
    

    def select_geol(self):
        """
        Function: Selection of geological formations based on their description
        """
        self.strati = self.list_strati.currentText()
        print("A: "+str(len(self.GDR_SILEX.selectedFeatures()))) # debudtest
        if len(self.GDR_SILEX.selectedFeatures())!=0:
            # Selection of geological formations by attribute from the description selected in the existing selection
            Fonctions_QGIS.Selectbyattribute(self, self.GDR_SILEX, self.attribute, self.strati, 3)
        else: # If a second type of geological formation is loaded
            admin = self.combo_wfs.currentText()
            # Selection by attribute of the administrative entity concerned in the administrative entities layer
            Fonctions_QGIS.Selectbyattribute(self, self.admin_layer, self.column_admin, admin, 0)
            # print("B: "+str(len(self.admin_layer.selectedFeatures()))) # debugtest
            # Selection by location of geological formations from selected administrative units
            Fonctions_QGIS.Selectbylocation(self, self.GDR_SILEX, self.admin_layer, True, 0)
            # print("C: "+str(len(self.GDR_SILEX.selectedFeatures()))) # debudtest
            # Attribute-based selection of geological formations from the description selected in the existence selection
            Fonctions_QGIS.Selectbyattribute(self, self.GDR_SILEX, self.attribute, self.strati, 3)
            # Deselection of entities to facilitate searches and avoid duplication
            # print("D: "+str(len(self.GDR_SILEX.selectedFeatures()))) # debudtest
            self.admin_layer.removeSelection()

        self.half_launch.setEnabled(True)
        self.check_cities.setEnabled(True)

        selected_features = len(self.GDR_SILEX.selectedFeatures())
        message = "Formation géologique sélectionnée (Nb selected_features: {}) (6/6)".format(selected_features)
        self.progres_label.setText(message)


    def load_cities(self):
        """
        Function: Loading the layer containing city names
        """
        try:
            self.cities_layer = Utilitaires.layer_finder(self.combo_cities)
            # Define the source of the drop-down list of features in the selected layer
            self.attribute_cities.setLayer(self.cities_layer)
            self.name_cities.setSourceLayer(self.cities_layer)

            self.name_cities.setEnabled(True)
            self.attribute_cities.setEnabled(True)
            self.ok_cities_2.setEnabled(True)

            message = "Sélectionnez la ville recherchée (7/8)"
            self.progres_label.setText(message)
        except:
            message = "La couche choisie n'a pas été trouvée, ou ne possède pas de champs utilisables"
            self.progres_label.setText(message)


    def select_attr_cities(self):
        """
        Function: Choice of identification field for towns and municipalities
        """
        self.name_cities.setSourceLayer(self.cities_layer)
        self.cities_attribute = self.attribute_cities.currentField()

        # The selection column has been changed to differentiate between towns with the same name.
        self.name_cities.setDisplayExpression(" \"{}\" ".format(self.cities_attribute))


    def select_cities(self):
        """
        Function: Select the city you are looking for in the city layer
        """
        self.cities_attribute = self.attribute_cities.currentField()
        # print(self.cities_attribute) debugtest
        city_name = self.name_cities.currentText()
        # print(city_name) #debugtest
        # Sélection dans la couche grâce à une expression donnée
        self.Ville = self.cities_layer.selectByExpression(" \"{}\" = '{}' ".format(self.cities_attribute, city_name))
        self.half_launch.setEnabled(True)

        message = "Ville sélectionnée (8/8)"
        self.progres_label.setText(message)
        

    def select_strati(self):
        """
        Function: Selection of geological formations according to whether or not a town is used
        """
        # Check whether the selection will be made in a particular city
        if self.check_cities.isChecked():
            if len(self.cities_layer.selectedFeatures()) == 0:
                self.select_cities()
            # Creation of a buffer zone around the town, corresponding to the distance within which the geological formations are most likely to be found
            self.Buffer = Fonctions_QGIS.Buffer(self, self.cities_layer, 10000, True, True)
            # Creation of an additional zone to prevent courses that are too far away from each other from being selected
            min = Fonctions_QGIS.Buffer(self, self.cities_layer, 12000, True, True)
            max = Fonctions_QGIS.Buffer(self, self.cities_layer, 30000, True, True)
            self.limite = Fonctions_QGIS.Difference(self, min, max)

            features = self.GDR_SILEX.selectedFeatures()
            # print("A nb select = "+str(len(features))) # debugtest
            if len(features) != 0:
                # Selection by location of geological formations from the buffer
                Fonctions_QGIS.Selectbylocation_simple(self, self.GDR_SILEX, self.Buffer, 2)

            features = self.GDR_SILEX.selectedFeatures()
            # print("B nb select = "+str(len(features))) # debugtest
            if len(features) != 0:
                # Selection by attributes of geological formations with the description sought in the selection made
                Fonctions_QGIS.Selectbyattribute(self, self.GDR_SILEX, self.attribute, self.strati, 3)

                # Récupération des éléments sélectionnés dans la couche
                features = self.GDR_SILEX.selectedFeatures()
                # print("C nb select = "+str(len(features))) # debugtest

            # Deselection of entities to facilitate searches and avoid duplication
            self.cities_layer.removeSelection()
            self.zone_layer.removeSelection()

            message = "Début de la sélection des entités"
            percent = 50
            Utilitaires.progression(self.bar_1, self.progres_label, message, percent)

        else:
            # print("A "+str(len(self.zone_layer.selectedFeatures()))) #debugtest
            if len(self.zone_layer.selectedFeatures()) != 0:
                # print("B "+str(len(self.GDR_SILEX.selectedFeatures()))) #debugtest
                buffer = Fonctions_QGIS.Buffer(self, self.zone_layer, 1000, True)
                # Selection by location of geological formations from the extraction zone selected in the existing selection
                Fonctions_QGIS.Selectbylocation_simple(self, self.GDR_SILEX, buffer, 0)
                # print("C "+str(len(self.GDR_SILEX.selectedFeatures()))) #debugtest
                if len(self.GDR_SILEX.selectedFeatures()) != 0:
                    # Attribute-based selection of geological formations from the description selected in the existence selection
                    Fonctions_QGIS.Selectbyattribute(self, self.GDR_SILEX, self.attribute, self.strati, 3)
            
            selected_features = len(self.GDR_SILEX.selectedFeatures())
            message = "Formation géologique sélectionnée (Nb selected_features: {}) (6/6)".format(selected_features)
            self.progres_label.setText(message)

            # Retrieving items selected in the layer
            features = self.GDR_SILEX.selectedFeatures()
            # print("b_nb select = "+str(len(features))) # debugtest

            # Deselection of entities to facilitate searches and avoid duplication
            self.zone_layer.removeSelection()

            selected_features = len(self.GDR_SILEX.selectedFeatures())
            message = "Début de la sélection des entités (Nb selected_features: {})".format(selected_features)
            percent = 50
            Utilitaires.progression(self.bar_1, self.progres_label, message, percent)

        name = self.layer_name.text()
        if name == "":
            name = "Extraction"

        # Check that one of the layers is not already loaded
        if not QgsProject.instance().mapLayersByName(name):
            # Creation of the intermediate layer containing the geological formations selected before aggregation
            geological_formation = Utilitaires.layer_creation(self, "Polygon", "3857", name, self.GDR_SILEX)
            # Définition du style de la couche
            style_path = os.path.dirname(os.path.abspath(__file__)) + '//Styles//Legende_CODE_HEXA_ETAGE_FINAL_vEV.qml'
            if os.path.exists(style_path):
                geological_formation.loadNamedStyle(style_path)
            else:
                pass
        
        # Select the layer by name
        geological_formation = QgsProject.instance().mapLayersByName(name)[0]
        # Addition of selected features to the intermediate layer
        layer_data = geological_formation.dataProvider()
        layer_data.addFeatures(features)
        
        # Check that you want to use a town for the selection process
        if self.check_cities.isChecked():
            # Using the city-specific selection function
            self.selection_with_city(geological_formation, layer_data)
            self.admin_layer.removeSelection()
        else:
            # Use of the selection function specific to extraction zones
            self.selection_with_zone(geological_formation)  
            self.admin_layer.removeSelection()

        try:
            if name == "Extraction":
                    for feat in geological_formation.getFeatures():
                        formation = feat["etage_final"]
                        break
                    if self.check_cities.isChecked():
                        ville = self.name_cities.currentText()
                        name = formation + "_" + ville
                    else:
                        zone = self.name_zone.currentText()
                        name = formation + "_" + zone
                    geological_formation.setName(name)
                    self.name = name
                    geological_formation.removeSelection()
            message = "Fin de la sélection des couches géologiques. Terminer la sélection ou lancer une nouvelle recherche"
            iface.messageBar().pushMessage("Succès:", "L'extraction a été réalisée avec succès", level=3)
            percent = 100
            Utilitaires.progression(self.bar_1, self.progres_label, message, percent)
        except:
            QgsProject.instance().removeMapLayer(geological_formation)
            message = "Une erreur c'est produite: Aucune formation n'a été extraite"
            iface.messageBar().pushMessage("Erreur:", "Aucune formation récupérable", level=1)
            percent = 0
            Utilitaires.progression(self.bar_1, self.progres_label, message, percent)

        # Deselection of entities to facilitate searches and avoid duplication
        self.GDR_SILEX.removeSelection()

        self.half_launch.setEnabled(False)
        self.final_launch.setEnabled(True)
        self.check_cities.setChecked(False)


    def Verif_loop(self, layer, WFS, attribute, strati):
        """
        Input:  layer (QgsVectorLayer) = layer of geological formations being completed
                WFS (wfs) = WFS flow where entities will be selected
                attribute (str) = name of reference field for selection
                strati (str) = Value for selection
        Function: Perform an entity selection round of a WFS flow for the layer currently being completed
        """
        # print("0") # debugtest
        # Creating a buffer around geological formations that are already isolated
        buffered_layer = Fonctions_QGIS.Buffer(self, layer, 1000, False, True)
        # print("1") # debugtest
        # Selection by location of geological formations intersecting the buffer in the existing selection
        Fonctions_QGIS.Selectbylocation_simple(self, WFS, buffered_layer, 0)
        # print("2") # debugtest
        # Attribute-based selection of geological formations based on the reference value in the existing selection
        Fonctions_QGIS.Selectbyattribute(self, WFS, attribute, strati, 3)
        # print("3") # debugtest
        # Erosion of already isolated geological formations
        buffered_layer = Fonctions_QGIS.Buffer(self, layer, -1, False, True)
        # Verification that users wish to use a town for the selection process
        if self.check_cities.isChecked():
            # Locate geological formations intersecting the boundary zone to deselect them
            Fonctions_QGIS.Selectbylocation_simple(self, WFS, self.limite, 3)
        # Location-based selection of geological formations intersecting erosion to deselect already isolated geological formations
        Fonctions_QGIS.Selectbylocation_simple(self, WFS, buffered_layer, 3)
        # print("4") # debugtest


    def selection_with_zone(self, geological_formation):
        """
        Input:  geological_formation (str) = intermediate layer containing selected geological formations prior to aggregation 
        Function: Sélection de formations géologiques complémentaires à partr de la zone d'extraction
        """
        # Retrieving items selected in the layer
        features = self.GDR_SILEX.selectedFeatures()
        # print("nb select = "+str(len(features))) # debugtest
        if len(features) != 0:
            # Using the verification loop to select additional training courses
            self.Verif_loop(geological_formation, self.GDR_SILEX, self.attribute, self.strati)
        

    def selection_with_city(self, geological_formation, layer_data):
        """
        Input:  geological_formation (str) = intermediate layer containing selected geological formations prior to aggregation 
                layer_data (data provider of a layer) = Access to data in the layer being modified
        Function: Selection of complementary geological formations from the city's buffer zone
        """
        repeater = True

        # Retrieving items selected in the layer
        features = self.GDR_SILEX.selectedFeatures()
        # print("AA nb select = "+str(len(features))) # debugtest
        if len(features) != 0:
            # Using the verification loop to select additional training courses
            self.Verif_loop(geological_formation, self.GDR_SILEX, self.attribute, self.strati)

        # Selection as long as additional courses in the zone can be selected
        while repeater:
            # Retrieving items selected in the layer
            features=self.GDR_SILEX.selectedFeatures()
            # print("T nb select = "+str(len(features))) # debugtest
            if len(features)>1:
                # Add selected entities to the layer being completed 
                layer_data.addFeatures(features)
                # Using the verification loop to select additional training courses
                self.Verif_loop(geological_formation, self.GDR_SILEX, self.attribute, self.strati)
            else:
                repeater = False            
        selected_features = len(self.GDR_SILEX.selectedFeatures())
        message = "Nombre de formations sélectionnées (Nb selected_features: {})".format(selected_features)
        percent = 80
        Utilitaires.progression(self.bar_1, self.progres_label, message, percent)
        
                    
    def final_layer(self):
        """
        Function: Creation of the final layer once the entities of each type of silicites have been aggregated
        """
        self.bar_1.reset()
        message = "Regroupement des polygones dans la couche finale"
        percent = 50
        Utilitaires.progression(self.bar_1, self.progres_label, message, percent)

        # Select the layer by name
        layer = QgsProject.instance().mapLayersByName(self.name)

        try:
            # Aggregation of intermediate layer entities to facilitate selections
            fusion = Fonctions_QGIS.Aggregation(self, layer[0])
            # Addition of an extra field to retain the name of the type of silica/geological stage
            fusion.dataProvider().addAttributes([QgsField("Nom_extraction", QVariant.String)])
            fusion.updateFields()

            # Extra field index retrieval
            field_idx = fusion.fields().indexOf('Nom_extraction')
            with edit(fusion):
                for feature in fusion.getFeatures():
                    # Changing the field value
                    fusion.changeAttributeValue(feature.id(), field_idx, self.name)

            # Check that one of the layers is not already loaded
            if QgsProject.instance().mapLayersByName("Couches_Geologiques"):
                # Select the layer by name
                new_layer = QgsProject.instance().mapLayersByName("Couches_Geologiques")[0]
            else:
                # Create the layer if it doesn't exist
                new_layer = Utilitaires.layer_creation(self, "Polygon", "3857", "Couches_Geologiques", fusion)
                # Defining the layer style
                style_path = os.path.dirname(os.path.abspath(__file__)) + '//Styles//Legende_CODE_HEXA_ETAGE_FINAL_vEV.qml'
                if os.path.exists(style_path):
                    new_layer.loadNamedStyle(style_path)
                else:
                    pass
                # Adding the layer to the map
                QgsProject.instance().addMapLayer(new_layer)
            
            # Retrieving entities from the aggregated layer
            features=fusion.getFeatures()
            # Add the selected entities to the final layer
            new_layer.dataProvider().addFeatures(features)
            iface.messageBar().pushMessage("Succès:", "L'ajout à la couche finale a été réalisé avec succès", level=3)
            message = "Avancement dans la récupération des formations géologiques"
        except:
            iface.messageBar().pushMessage("Erreur:", "La couche à intégrer n'existe plus ou son name a été modifié. Impossible de l'intégrer à la couche finale.", level=2)
            message = "La couche à intégrer n'existe plus ou son name a été modifié. Impossible de l'intégrer à la couche finale."
        
        self.progres_label.setText(message)
        self.bar_1.reset()

        self.combo_feature.clear()
        self.combo_feature.clearEditText()
        self.name_zone.clear()
        self.name_zone.clearEditText()
        self.combo_wfs.clear()
        self.list_strati.clear()
        self.layer_name.clear()

        self.combo_feature.setEnabled(False)
        self.name_zone.setEnabled(False)
        self.ok_zone_2.setEnabled(False)
        self.combo_admin.setEnabled(False)
        self.ok_admin.setEnabled(False)
        self.select_admin.setEnabled(False)
        self.ok_zone_3.setEnabled(False)
        self.launch.setEnabled(False)
        self.name_admin.setEnabled(False)
        self.combo_wfs.setEnabled(False)
        self.ok_wfs.setEnabled(False)
        self.choice_1.setChecked(True)
        self.list_strati.setEnabled(False)
        self.ok_strati.setEnabled(False)
        self.final_launch.setEnabled(False)
