# -*- coding: utf-8 -*-
"""
/***************************************************************************
 SilLCP
                                 A QGIS plugin
 Plugin pour la création de LCP à partir des outils de la librairie movecost sur les bases du plugin MovecostToQGIS
 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
import shutil
import processing
from processing.core.ProcessingConfig import ProcessingConfig
from qgis import *
from qgis.PyQt import *
from qgis.core import *
from qgis.utils import *
from qgis.gui import *
from qgis.PyQt.QtGui import *
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtWidgets import *
from qgis.PyQt.QtCore import *

from ..SiliciteR.Utils import ParamsR
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__), 'SilLCP_dialog_base.ui'))


class SilLCPDialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, parent=None):
        """Constructor."""
        super(SilLCPDialog, 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)
        self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowCloseButtonHint)

        # Connecting the signal of closing the olugin to a function
        self.finished.connect(self.on_finished)

        """Initialisation of some functions and tools"""
        self.combo_site.setFilters(QgsMapLayerProxyModel.PointLayer)
        self.combo_polygons.setFilters(QgsMapLayerProxyModel.PolygonLayer)
        self.combo_origin.setFilters(QgsMapLayerProxyModel.PointLayer)
        self.combo_nodes.setFilters(QgsMapLayerProxyModel.PointLayer)
        self.combo_star.setFilters(QgsMapLayerProxyModel.PointLayer)

        self.dossier_R.setStorageMode(QgsFileWidget.StorageMode.GetDirectory)
        self.scripts_R.setStorageMode(QgsFileWidget.StorageMode.GetDirectory)
        self.librairies_R.setStorageMode(QgsFileWidget.StorageMode.GetDirectory)

        """Preparations for the first use of SiliciteR"""
        R_parameters = QSettings().value("Siligites/R_parameters", False)
        if not R_parameters:
            self.tabWidget.setTabEnabled(1,False)
            self.LCP.setEnabled(False)
            self.network.setEnabled(False)
            self.corridors.setEnabled(False)
            self.isochrones.setEnabled(False)
            self.LCP.setEnabled(False)
            self.network.setEnabled(False)
            self.corridors.setEnabled(False)
            self.isochrones.setEnabled(False)
            self.prp.setEnabled(False)
            if not ParamsR.is_windows():
                self.label_26.hide()
                self.version_64.hide()

                self.setMinimumSize(0, 0)
                new_height = self.minimumSizeHint().height()
                self.resize(self.width(), new_height)

        """Linking buttons and functions"""
        self.validation.clicked.connect(self.maj_provider)
        self.ok_site.clicked.connect(self.select_layer_extraction)
        self.combo_attribute.fieldChanged.connect(self.select_attribute)
        self.ok_1.clicked.connect(self.extraction_origin)
        self.ok_polygones.clicked.connect(self.select_polygones_layer)
        self.ok_origin.clicked.connect(self.select_reference_point)
        self.ok_2.clicked.connect(self.select_type)
        self.full.toggled.connect(self.switch_network_type)
        self.star.toggled.connect(self.switch_network_type)
        self.combo_nodes.layerChanged.connect(self.check_nodes)
        self.combo_star.layerChanged.connect(self.check_nodes)
        self.ok_3.clicked.connect(self.create_lines)
        self.original.toggled.connect(self.find_sources)
        self.prp.toggled.connect(self.find_sources)
        self.detec.toggled.connect(self.find_sources)
        self.manuel.toggled.connect(self.find_sources)
        self.original.toggle()

    """Gestion of the correct loading and unloading the plugin"""
    # Launching the reset of the parameters when the plugin is launched
    def showEvent(self, event: QShowEvent):
        self.recharge()
    
    # Launching the reset of the parameters when the plugin is closed
    def on_finished(self):   
        self.recharge()

    # Reseting the parameters of the plugin when it is shut down for the next uses
    def recharge(self):   
        self.combo_attribute.setEnabled(False)
        self.combo_start.setEnabled(False)
        self.ok_1.setEnabled(False)
        self.combo_origin.setEnabled(False)
        self.ok_origin.setEnabled(False)
        self.p_furthest.setEnabled(False)
        self.p_centroide.setEnabled(False)
        self.p_closest.setEnabled(False)
        self.ok_2.setEnabled(False)
        self.add_origin.setChecked(False)
        self.add_origin.setEnabled(False)

        self.indicator_polygons.hide()
        self.indicator_polygons.setEnabled(False)
        self.indicator_polygons.setChecked(False)
        self.indicator_origin.hide()
        self.indicator_origin.setChecked(False)
        self.indicator_origin.setAutoExclusive(True)
        self.indicator_origin.setEnabled(False)
        self.indicator_origin.setStyleSheet("")
        self.full.setChecked(True)
        self.combo_star.setEnabled(False)
        self.indicator_center.hide()
        self.indicator_center.setEnabled(False)
        self.indicator_ends.hide()
        self.indicator_ends.setChecked(False)

        self.setMinimumSize(0, 0)
        self.resize(self.minimumSizeHint())

        self.check_nodes()

    @staticmethod
    def tr(message):
        return QCoreApplication.translate("SilLCPDialogBase", message)

    def find_sources(self):
        """
        Preparation to show the different parameters of SiliciteR depending of the source
        """
        self.prp.setEnabled(False)
        # Verification if Processing R Provider is installed to get possible already completed parameters
        profile_home = QgsApplication.qgisSettingsDirPath()
        processingR = os.path.exists(os.path.join(profile_home, 'python', 'plugins', 'processing_r'))

        # First step, does the plugin is installed ?
        try:
            if processingR:
                from processing_r.processing.utils import RUtils
                # Second step, does the plugin is activated to access the parameters
                if ProcessingConfig.getSetting(RUtils.R_FOLDER) is not None:
                    self.prp.setEnabled(True)
        except Exception:
            pass
        
        # First case, using or checking the parameters saved inside Siligîtes
        if self.original.isChecked():
            self.dossier_R.setFilePath(ProcessingConfig.getSetting(ParamsR.DOSSIER_R))
            self.scripts_R.setFilePath(ProcessingConfig.getSetting(ParamsR.DOSSIER_SCRIPTS_R))
            self.librairies_R.setFilePath(ProcessingConfig.getSetting(ParamsR.LIBS_R))
            self.cran.setText(ProcessingConfig.getSetting(ParamsR.DEPOT_R))
            self.user_folder.setChecked(ProcessingConfig.getSetting(ParamsR.USER_LIB))
            if ParamsR.is_windows():
                self.version_64.setChecked(ProcessingConfig.getSetting(ParamsR.R64))

        # Second case, using the parameters of Processing R Provider
        if self.prp.isChecked():
            self.dossier_R.setFilePath(ProcessingConfig.getSetting(RUtils.R_FOLDER))
            self.scripts_R.setFilePath(ProcessingConfig.getSetting(RUtils.RSCRIPTS_FOLDER))
            self.librairies_R.setFilePath(ProcessingConfig.getSetting(RUtils.R_LIBS_USER))
            self.cran.setText(ProcessingConfig.getSetting(RUtils.R_REPO))
            self.user_folder.setChecked(ProcessingConfig.getSetting(RUtils.R_USE_USER_LIB))
            if ParamsR.is_windows():
                self.version_64.setChecked(ProcessingConfig.getSetting(RUtils.R_USE64))

        # Third case, using the differents functions to detect the possible paths
        if self.detec.isChecked():
            self.dossier_R.setFilePath(ParamsR.locate_r("R"))
            self.scripts_R.setFilePath(ParamsR.default_scripts_folder())
            self.librairies_R.setFilePath(ParamsR.locate_r("Rscript"))
            self.cran.setText(ParamsR.repository())
            self.user_folder.setChecked(bool(ParamsR.user_libs()))
            if ParamsR.is_windows():
                self.version_64.setChecked(bool(ParamsR.R_64()))

        # Fourth case, reset every parameters to manually completing them
        if self.manuel.isChecked():
            self.dossier_R.setFilePath("")
            self.scripts_R.setFilePath("")
            self.librairies_R.setFilePath("")
            self.cran.setText("")
            self.user_folder.setChecked(False)
            self.version_64.setChecked(False)


    def maj_provider(self):
        """
        Update provider SiliciteR parameters and allow the use of Movecost tools
        """
        # Accessing the provider of Siligîtes
        provider = QgsApplication.processingRegistry().providerById("siliciter")

        # Modifying the R folder parameter if necessary
        if self.dossier_R.filePath() != ProcessingConfig.getSetting(ParamsR.DOSSIER_R):
            ProcessingConfig.setSettingValue(ParamsR.DOSSIER_R, self.dossier_R.filePath())
            ProcessingConfig.readSettings()
            provider.refreshAlgorithms()
            QSettings().setValue("Siligites/DOSSIER_R", self.dossier_R.filePath())

        # Modifying the R scipts folder parameter if necessary
        if self.scripts_R.filePath() != ProcessingConfig.getSetting(ParamsR.DOSSIER_SCRIPTS_R):
            ProcessingConfig.setSettingValue(ParamsR.DOSSIER_SCRIPTS_R, self.scripts_R.filePath())
            ProcessingConfig.readSettings()
            provider.refreshAlgorithms()
            QSettings().setValue("Siligites/DOSSIER_SCRIPTS_R", self.scripts_R.filePath())

        # Modifying the R libraries folder parameter if necessary
        if self.librairies_R.filePath() != ProcessingConfig.getSetting(ParamsR.LIBS_R):
            ProcessingConfig.setSettingValue(ParamsR.LIBS_R, self.librairies_R.filePath())
            ProcessingConfig.readSettings()
            provider.refreshAlgorithms()
            QSettings().setValue("Siligites/LIBS_R", self.librairies_R.filePath())

        # Modifying the R libraries repository parameter if necessary
        if self.cran.text() != ProcessingConfig.getSetting(ParamsR.DEPOT_R):
            ProcessingConfig.setSettingValue(ParamsR.DEPOT_R, self.cran.text())
            ProcessingConfig.readSettings()
            provider.refreshAlgorithms()
            QSettings().setValue("Siligites/DEPOT_R", self.cran.text())

        # Modifying the choice of using the user R librairies folder if necessary
        if self.user_folder.isChecked() != ProcessingConfig.getSetting(ParamsR.USER_LIB):
            ProcessingConfig.setSettingValue(ParamsR.USER_LIB, self.user_folder.isChecked())
            ProcessingConfig.readSettings()
            provider.refreshAlgorithms()
            QSettings().setValue("Siligites/USER_LIB", self.user_folder.isChecked())

        # Modifying the choice of using the 64bit version of Windows if necessary
        if self.version_64.isChecked() != ProcessingConfig.getSetting(ParamsR.R64) and ParamsR.is_windows():
            ProcessingConfig.setSettingValue(ParamsR.R64, self.version_64.isChecked())
            ProcessingConfig.readSettings()
            provider.refreshAlgorithms()
            QSettings().setValue("Siligites/R64", self.version_64.isChecked())

        # Giving access to the Movecost algorithms after completion
        QSettings().setValue("Siligites/R_parameters", True)
        self.original.toggle()
        self.tabWidget.setTabEnabled(1,True)
        self.tabWidget.setCurrentIndex(1)
        self.LCP.setEnabled(True)
        self.network.setEnabled(True)
        self.corridors.setEnabled(True)
        self.isochrones.setEnabled(True)


    """
    All scripts called up during analyses
    """    
    def on_movecost_pressed(self):
        self.access_Script("siligitesmovecost")
        processing.execAlgorithmDialog('siliciter:siligitesmovecost')

    def on_movebound_pressed(self):
        self.access_Script("siligitesmovebound")
        processing.execAlgorithmDialog('siliciter:siligitesmovebound')

    def on_movecorr_pressed(self):
        self.access_Script("siligitesmovecorr")
        processing.execAlgorithmDialog('siliciter:siligitesmovecorr')

    def on_movealloc_pressed(self):
        self.access_Script("siligitesmovealloc")
        processing.execAlgorithmDialog('siliciter:siligitesmovealloc')

    def on_movecomp_pressed(self):
        self.access_Script("siligitesmovecomp")
        processing.execAlgorithmDialog('siliciter:siligitesmovecomp')

    def on_movenetw_pressed(self):
        self.access_Script("siligitesmovenetw")
        processing.execAlgorithmDialog('siliciter:siligitesmovenetw')

    def on_moverank_pressed(self):
        self.access_Script("siligitesmoverank")
        processing.execAlgorithmDialog('siliciter:siligitesmoverank')

    def on_LCP_pressed(self):
        self.access_Script("siligitesmovecost")
        processing.execAlgorithmDialog('siliciter:siligitesmovecost')

    def on_network_pressed(self):
        self.access_Script("siligitesmovenetw")
        processing.execAlgorithmDialog('siliciter:siligitesmovenetw')

    def on_corridors_pressed(self):
        self.access_Script("siligitesmovecorr")
        processing.execAlgorithmDialog('siliciter:siligitesmovecorr')

    def on_isochrones_pressed(self):
        self.access_Script("siligitesmovebound")
        processing.execAlgorithmDialog('siliciter:siligitesmovebound')


    def access_Script(self, function):
        """
        Makes a copy of the scripts so that the user can use them and be sure that the scripts are up to date

        :param function: Function to copy from the internal folder to the used folder
        :type function: str

        :returns: Translated string
        :rtypes: str
        """
        # Access to the folder where R scripts are stored in the module
        a = 'SiliciteR//rscripts'
        folder = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        # print(folder) # debugtest
        source_profile = os.path.join(folder, a)
        # print(source_profile) # debugtest

        # Access to the folder where they must be copied to make the module work
        scripts = ProcessingConfig.getSetting(ParamsR.DOSSIER_SCRIPTS_R)
        # print(rs) # debugtest

        # Check that the file exists
        if not os.path.exists(scripts):
            os.makedirs(scripts)

        # Files are copied at the time of use, allowing them to be updated automatically in the event of changes
        for filename in os.listdir(source_profile):
            if ".rsx" in str(filename):
                # print(str(filename)) # debugtest
                source_file = os.path.join(source_profile, filename)
                dest_file = os.path.join(scripts, filename)
                # print(dest_file) # debugtest

                shutil.copy2(source_file, dest_file)
        # print(os.listdir(scripts)) # debugtest

        registry = QgsApplication.processingRegistry()
        for provider in registry.providers():
            provider.refreshAlgorithms()


    def select_layer_extraction(self):
        """
        Selection of the layer from which the origin and destination(s) of the PCLs are to be extracted
        """
        self.site_layer = Utilitaires.layer_finder(self.combo_site)
        # Définition de la source de la list déroulante des entités de la couche sélectionnée
        self.combo_attribute.setLayer(self.site_layer)

        self.combo_attribute.setEnabled(True)
        self.combo_start.setEnabled(True)
        self.ok_1.setEnabled(True)


    def select_attribute(self):
        """
        Selecting an attribute field for selecting the point of origin of LCPs
        """
        self.attribute = self.combo_attribute.currentField()
        # Changement de champ de référence pour le choix de l'entité
        self.combo_start.setLayer(self.site_layer)
        self.combo_start.setFilterExpression("")
        self.combo_start.setDisplayExpression(self.attribute)


    def extraction_origin(self):
        """
        Extraction of the LCP point of origin, and creation of a destination layer(s) from the remaining points
        """
        self.attribute = self.combo_attribute.currentField()
        origin = self.combo_start.feature()
        # print(origin) # debugtest

        progress_dialog = QProgressDialog(self.tr("Extraction en cours..."), None, 0, 0)
        progress_dialog.setWindowModality(Qt.WindowModality.WindowModal)
        progress_dialog.setWindowTitle(self.tr("Veuillez patienter"))
        progress_dialog.setWindowModality(Qt.WindowModality.ApplicationModal)
        progress_dialog.setCancelButton(None)
        progress_dialog.setValue(0)
        progress_dialog.show()
        QCoreApplication.processEvents()

        # Preparation of a layer group to put all the layers in the same place
        group = QgsProject.instance().layerTreeRoot().findGroup(self.tr("Extraction_depuis_{}").format(self.site_layer.name()))
        if group is None:
            group = QgsProject.instance().layerTreeRoot().insertGroup(0, self.tr("Extraction_depuis_{}").format(self.site_layer.name()))
            group.setExpanded(True)

        # Recover the SCR from the input layer to use the same in the output layers
        SCR = self.site_layer.crs().authid().replace("EPSG:", "")
        # print(self.site_layer.crs().authid().replace("EPSG:","").replace(" ","")) # debugtest

        # Creation of layers containing the start point and end point(s)
        origin_layer = Utilitaires.layer_creation("Point", SCR, origin[str(self.attribute)], self.site_layer)

        # Adding the layer to the group
        QgsProject.instance().addMapLayer(origin_layer, False)
        if group.findLayer(origin_layer.id()) is None:
            group.addLayer(origin_layer)

        name = self.tr("{}_sans_{}").format(self.site_layer.name(),origin[str(self.attribute)])
        destination_layer = Utilitaires.layer_creation("Point", SCR, name, self.site_layer)
        # Adding the layer to the group
        QgsProject.instance().addMapLayer(destination_layer, False)
        if group.findLayer(destination_layer.id()) is None:
            group.addLayer(destination_layer)

        # Access the layer data and add the selected entity
        layer_data = origin_layer.dataProvider()
        layer_data.addFeature(origin)

        # Select all the other points in the layer, then remove the start point to obtain the end point(s)
        selected_ids = None
        if len(self.site_layer.selectedFeatures()) != 0:
            selected_ids = self.site_layer.selectedFeatureIds()
        self.site_layer.selectAll()

        Fonctions_QGIS.Selectbylocation_simple(self.site_layer, origin_layer, 3)
        features = self.site_layer.selectedFeatures()

        layer_data = destination_layer.dataProvider()
        layer_data.addFeatures(features)

        self.site_layer.removeSelection()
        if selected_ids != None:
            self.site_layer.selectByIds(selected_ids)

        # Possibility of sorting points by distance if enabled
        # self.t12km = Fonctions_QGIS.Buffer(origin_layer, 12000, False, False)
        # self.t100km = Fonctions_QGIS.Buffer(origin_layer, 100000, False, False)
        # Division of the entities in the layer according to their distance from the LCP origin
        # self.division(destination_layer)

        self.combo_attribute.setLayer(None)
        self.combo_attribute.setEnabled(False)
        self.combo_start.setFilterExpression("1 = 0")
        self.combo_start.setEnabled(False)
        self.ok_1.setEnabled(False)

        progress_dialog.close()


    def select_polygones_layer(self):
        """
        Select a layer of polygons to simplify into points
        """
        self.polygon_layer = Utilitaires.layer_finder(self.combo_polygons)
        self.combo_origin.setEnabled(True)
        self.ok_origin.setEnabled(True)
        self.p_centroide.setEnabled(True)
        self.ok_2.setEnabled(True)

        self.p_furthest.setEnabled(False)
        self.p_closest.setEnabled(False)
        self.add_origin.setChecked(False)
        self.add_origin.setEnabled(False)

        self.indicator_origin.hide()
        self.indicator_origin.setChecked(False)
        self.indicator_origin.setAutoExclusive(True)
        self.indicator_origin.setEnabled(False)
        self.indicator_origin.setStyleSheet("")

        self.origin_layer = None

        if len(self.polygon_layer.selectedFeatures()) > 0:
            self.indicator_polygons.show()
            self.indicator_polygons.setEnabled(True)
            self.indicator_polygons.setChecked(True)
            self.indicator_polygons.setStyleSheet("""
            QCheckBox::indicator {
                border: 2px solid black;
            }
            QCheckBox::indicator:checked {
                background-color: gold;
            }
            """)
        else:
            self.indicator_polygons.hide()
            self.indicator_polygons.setEnabled(False)
            self.indicator_polygons.setChecked(False)


    def select_reference_point(self):
        """
        Selection of the layer containing the reference point
        """
        self.origin_layer = Utilitaires.layer_finder(self.combo_origin)

        if len(self.origin_layer.selectedFeatures()) == 1 or self.origin_layer.featureCount() == 1:
            self.p_furthest.setEnabled(True)
            self.p_closest.setEnabled(True)
            self.ok_2.setEnabled(True)
            self.add_origin.setEnabled(True)

            if len(self.origin_layer.selectedFeatures()) == 1:
                self.indicator_origin.show()
                self.indicator_origin.setChecked(True)
                self.indicator_origin.setAutoExclusive(False)
                self.indicator_origin.setEnabled(False)
                self.indicator_origin.setStyleSheet("QRadioButton::indicator { background-color: gold; }")
            else:
                self.indicator_origin.show()
                self.indicator_origin.setChecked(False)
                self.indicator_origin.setAutoExclusive(False)
                self.indicator_origin.setEnabled(False)
                self.indicator_origin.setStyleSheet("QRadioButton::indicator { background-color: green; }")
        else:
            self.p_furthest.setEnabled(False)
            self.p_closest.setEnabled(False)
            self.add_origin.setChecked(False)
            self.add_origin.setEnabled(False)

            self.indicator_origin.show()
            self.indicator_origin.setChecked(True)
            self.indicator_origin.setAutoExclusive(False)
            self.indicator_origin.setEnabled(False)
            self.indicator_origin.setStyleSheet("QRadioButton::indicator { background-color: red; }")


    def select_type(self):
        """
        Create a point layer from a polygon layer (centroids, nearest and furthest points)
        """
        progress_dialog = QProgressDialog(self.tr("Extraction des points en cours..."), None, 0, 0)
        progress_dialog.setWindowModality(Qt.WindowModality.WindowModal)
        progress_dialog.setWindowTitle(self.tr("Veuillez patienter"))
        progress_dialog.setWindowModality(Qt.WindowModality.ApplicationModal)
        progress_dialog.setCancelButton(None)
        progress_dialog.setValue(0)
        progress_dialog.show()
        QCoreApplication.processEvents()

        # print("start") # debugtest
        # Possibilité de trier les points en function d'une distance si activée
        # # Création des tampons correspondant aux domaines proches et à la limite du domaine lointain
        # self.t12km = Fonctions_QGIS.Buffer(self.origin_layer, 12000, False, False)
        # self.t100km = Fonctions_QGIS.Buffer(self.origin_layer, 100000, False, False)

        # Check the SCRs of the two layers to ensure they are the same for the distance calculation
        SCR1 = self.polygon_layer.crs().authid().replace("EPSG:", "")

        # Check if entities are selected
        full = False
        selected_ids = None
        if len(self.polygon_layer.selectedFeatures()) == 0:
            full = True
            self.polygon_layer.selectAll()
        else:
            if not self.indicator_polygons.isChecked():
                selected_ids = self.polygon_layer.selectedFeatureIds()
                self.polygon_layer.selectAll()

        count = 0
        if self.p_centroide.isChecked():
            count += 1
        if self.p_closest.isChecked():
            count += 1
        if self.p_furthest.isChecked():
            count += 1
        
        if count > 1:
            group = QgsProject.instance().layerTreeRoot().findGroup(self.tr("Points_extraits_de_{}").format(self.polygon_layer.name()))
            if group is None:
                group = QgsProject.instance().layerTreeRoot().insertGroup(0, self.tr("Points_extraits_de_{}").format(self.polygon_layer.name()))
                group.setExpanded(True)

        origin = None
        if self.add_origin.isChecked():
            if self.origin_layer != None:
                if len(self.origin_layer.selectedFeatures()) == 0: 
                    origin = list(self.origin_layer.getFeatures())
                elif len(self.origin_layer.selectedFeatures()) == 1: 
                    origin = list(self.origin_layer.selectedFeatures())

        """Creation of the centroid layer if selected"""            
        if self.p_centroide.isChecked():
            # Using the QGIS centroid tool
            centroids = Fonctions_QGIS.Centroide(self.polygon_layer)

            # Preventing problems for futur usage
            if self.p_furthest.isEnabled() or self.p_closest.isEnabled():
                if centroids.crs().authid().replace("EPSG:","") != self.origin_layer.crs().authid().replace("EPSG:", ""):
                    centroids = Fonctions_QGIS.Reprojection(centroids, self.origin_layer.crs().authid().replace("EPSG:", ""))
                
            if origin != None:
                centroids.dataProvider().addFeatures(origin)

            centroids.setName("Centroides")
            if count > 1:
                QgsProject.instance().addMapLayer(centroids, False)
                group.addLayer(centroids)
            else:
                QgsProject.instance().addMapLayer(centroids)

            # Possibility of sorting points by distance if enabled
            # Division of layer entities according to their distance from the LCP origin
            # self.division(centroids)

        """Creation, if selected, of the layer(s) with the closest and/or the furthest points of the polygon(s) from the reference"""
        if self.p_furthest.isChecked() or self.p_closest.isChecked():
            # Getting the coordinates of the reference point
            if not self.indicator_origin.isChecked():
                self.origin_layer.selectAll()

            if len(self.origin_layer.selectedFeatures()) > 1:
                iface.messageBar().pushMessage(self.tr("Erreur:"), self.tr("Plus d'un point sélectionné ou sélectionnable dans la couche"), level=Qgis.MessageLevel.Critical)
                return

            # Creation of the closest points layer
            if self.p_closest.isChecked():
                make_closest = True
            else:
                make_closest = False
            if self.p_furthest.isChecked():
                make_furthest = True
            else:
                make_furthest = False
            
            closest, furthest = Fonctions_QGIS.ClosestFurthestPoints(self.polygon_layer, self.origin_layer, make_closest, make_furthest)

            if closest != None:
                if self.add_origin.isChecked():                
                    if origin != None:
                        feat = QgsFeature(closest.fields())
                        feat.setGeometry(origin[0].geometry())

                        closest.startEditing()
                        closest.addFeature(feat)
                        closest.commitChanges()

                if count > 1:
                    QgsProject.instance().addMapLayer(closest, False)
                    group.addLayer(closest)
                else:
                    QgsProject.instance().addMapLayer(closest)
            
            if furthest != None:
                if self.add_origin.isChecked():                
                    if origin != None:
                        feat = QgsFeature(furthest.fields())
                        feat.setGeometry(origin[0].geometry())

                        furthest.startEditing()
                        furthest.addFeature(feat)
                        furthest.commitChanges()

                if count > 1:
                    QgsProject.instance().addMapLayer(furthest, False)
                    group.addLayer(furthest)
                else:
                    QgsProject.instance().addMapLayer(furthest)
        
        if full:
            self.polygon_layer.removeSelection()
        if selected_ids != None:
            self.polygon_layer.removeSelection()
            self.polygon_layer.selectByIds(selected_ids)
        
        if not self.indicator_origin.isChecked():
            self.origin_layer.removeSelection()

        progress_dialog.close()

        self.combo_origin.setEnabled(False)
        self.ok_origin.setEnabled(False)
        self.p_furthest.setChecked(False)
        self.p_centroide.setChecked(False)
        self.p_closest.setChecked(False)
        self.p_furthest.setEnabled(False)
        self.p_centroide.setEnabled(False)
        self.p_closest.setEnabled(False)
        self.ok_2.setEnabled(False)
        self.add_origin.setChecked(False)
        self.add_origin.setEnabled(False)
        self.indicator_polygons.hide()
        self.indicator_polygons.setEnabled(False)
        self.indicator_polygons.setChecked(False)
        self.indicator_origin.hide()
        self.indicator_origin.setChecked(False)
        self.indicator_origin.setAutoExclusive(True)
        self.indicator_origin.setEnabled(False)
        self.indicator_origin.setStyleSheet("")


    def division(self, layer):
        """
        Add a field and divide entities in a layer according to their distance from the PCL origin
        (Not maintained, as it is specific to a study being carried out, but can be modified)
        
        :param layer: Layer to modify
        :type layer: QgsVectorLayer

        :returns: Points closest to and furthest from the origin of the LCP
        :rtypes: QgsVectorLayer
        """

        if self.p_furthest.isChecked() or self.p_closest.isChecked():
            # Deletion of columns in the attribute table that were created during the process but are no longer required 
            count = layer.fields().count()
            ind_list = list((count-1, count-2, count-3, count-4, count-5, count-6))

            # Remove multiple fields with a list of indices
            layer.dataProvider().deleteAttributes(ind_list)
            layer.updateFields()

        # # Accès aux données de la couche
        # layer_data = layer.dataProvider()
        # # Création d'un nouveau champ
        # layer_data.addAttributes([QgsField("Classe_Distance", QVariant.String)])
        # layer.updateFields()
        # # Récupération de l'id du champ pour sa complétion
        # field_idx = layer.fields().indexOf("Classe_Distance")

        # # Sélection des points dans le domaine proche (moins de 12km)
        # Fonctions_QGIS.Selectbylocation_simple(layer,self.t12km,0)
        # with edit(layer):
        #     for feature in layer.selectedFeatures():
        #         # Changement de la valeur du champ
        #         layer.changeAttributeValue(feature.id(), field_idx, "<12km")
        
        # # Sélection des points dans la zone intermédiaire (entre 12 et 100km)
        # Fonctions_QGIS.Selectbylocation_simple(layer,self.t100km,0)
        # Fonctions_QGIS.Selectbylocation_simple(layer,self.t12km,3)
        # with edit(layer):
        #     for feature in layer.selectedFeatures():
        #         # Changement de la valeur du champ
        #         layer.changeAttributeValue(feature.id(), field_idx, "12km->100km")

        # # Sélection des points dans le domaine lointain
        # layer.selectAll()
        # Fonctions_QGIS.Selectbylocation_simple(layer,self.t100km,3)
        # with edit(layer):
        #     for feature in layer.selectedFeatures():
        #         # Changement de la valeur du champ
        #         layer.changeAttributeValue(feature.id(), field_idx, ">100km")

        # Suppresion de la sélection
        layer.removeSelection()


    def switch_network_type(self):
        """
        Function to switch between modes of networks
        """
        if self.full.isChecked():
            self.label_specific.setText(QCoreApplication.translate("SilLCPDialogBase", "Noeuds du réseau :"))
            message = self.tr("Couche vecteur de points à relier avec des lignes droites")
            self.label_specific.setToolTip('<p style="white-space: pre-wrap; width:200px;">'+message+"</p>")
            self.label_specific.setWhatsThis('<p style="white-space: pre-wrap; width:200px;">'+message+"<p>")
            self.combo_nodes.setToolTip('<b>'+message+'</b>')
            self.combo_nodes.setWhatsThis('<b>'+message+'</b>')
            self.label_specific2.setText("")
            self.full.setChecked(False)
            self.combo_star.hide()
            self.combo_star.setEnabled(False)
            self.indicator_center.hide()
            self.indicator_center.setEnabled(False)
            self.indicator_center.setChecked(False)
            self.indicator_ends.hide()
            self.indicator_ends.setEnabled(False)
            self.indicator_ends.setChecked(False)

        if self.star.isChecked():
            self.label_specific.setText(QCoreApplication.translate("SilLCPDialogBase", "Noeud central :"))
            message ="\n".join([self.tr("Couche vecteur de points avec le noeud servant de centre pour réseau de lignes droites en étoile"),
                                self.tr("Il est possible d'utiliser soit une couche ne contenant qu'une entité(en utilisant par exemple le premier outil de ce plugin),"),
                                self.tr("soit d'en utiliser une avec plusieurs entités, mais en sélectionnant une en avance")
                ])
            self.label_specific.setToolTip('<p style="white-space: pre-wrap; width:200px;">'+message+"</p>")
            self.label_specific.setWhatsThis('<p style="white-space: pre-wrap; width:200px;">'+message+"</p>")
            self.combo_nodes.setToolTip('<b>'+message+'<b>')
            self.combo_nodes.setWhatsThis('<b>'+message+'<b>')
            self.label_specific2.setText(QCoreApplication.translate("SilLCPDialogBase", "Noeuds à relier :"))
            self.star.setChecked(True)
            self.combo_star.show()
            self.combo_star.setEnabled(True)

        self.check_nodes()
        
    
    def check_nodes(self):
        """
        Verification if the layers selected for the star shaped networks have selected features
        to warn the user and give a choice to only use or not those features
        """
        combos = [self.combo_nodes]
        indicators = [self.indicator_center]
        if self.star.isChecked():
            combos.append(self.combo_star)
            indicators.append(self.indicator_ends)

        for n in range(0, len(combos)):
            combo = combos[n]
            indicator = indicators[n]
            try:
                layer = Utilitaires.layer_finder(combo)
                if len(layer.selectedFeatures()) > 0:
                    indicator.show()
                    indicator.setEnabled(True)
                    indicator.setChecked(True)
                    indicator.setStyleSheet("""
                    QCheckBox::indicator {
                        border: 2px solid black;
                    }
                    QCheckBox::indicator:checked {
                        background-color: gold;
                    }
                    """)
                else:
                    indicator.hide()
                    indicator.setEnabled(False)
                    indicator.setChecked(False)
            except Exception:
                pass
            

    def create_lines(self):
        """
        Create a network of straight lines between several points, 
        or between one or more origins and several destinations
        """
        # Creation of the loading window
        self.progress_dialog = QProgressDialog(self.tr("Préparation de la création du réseau..."),
                                        None, 0, 100)
        self.progress_dialog.setWindowModality(Qt.WindowModality.WindowModal)
        self.progress_dialog.setWindowTitle(self.tr("Veuillez patienter"))
        self.progress_dialog.setWindowModality(Qt.WindowModality.ApplicationModal)
        self.progress_dialog.setCancelButton(None)
        self.progress_dialog.setValue(0)
        self.progress_dialog.show()
        QCoreApplication.processEvents()
        
        try:
            nodes = Utilitaires.layer_finder(self.combo_nodes)
        except Exception:
            self.progress_dialog.close()
            iface.messageBar().pushMessage(self.tr("Erreur:"), self.tr("Le/la couche nécessaire à la création du réseau n'a pas été trouvée. Veuillez vérifier les couches séectionnées"), level=Qgis.MessageLevel.Critical)
            return

        SCR = nodes.crs().authid().replace("EPSG:","")
        new_layer=QgsVectorLayer("{}?crs=epsg:{}".format('LineString',SCR), "Reseau_droites", "memory")
        layer_data = new_layer.dataProvider()

        if self.full.isChecked():
            layer_data.addAttributes([QgsField("id", QVariant.String),QgsField("id_couple", QVariant.String),QgsField("id_origine", QVariant.Int),QgsField("id_destination", QVariant.Int),QgsField("distance_km", QVariant.Double)])
            name = self.tr("Reseau_droites_complet_{}")
            new_layer.setName(name.format(nodes.name()))

        if self.star.isChecked():
            try:
                ends = Utilitaires.layer_finder(self.combo_star)
            except Exception:
                self.progress_dialog.close()
                iface.messageBar().pushMessage(self.tr("Erreur:"), self.tr("Le/la couche nécessaire à la création du réseau n'a pas été trouvée. Veuillez vérifier les couches séectionnées"), level=Qgis.MessageLevel.Critical)
                return    

            # Preventing problems of projections if different
            if ends.crs().authid().replace("EPSG:","") != SCR:
                tempo = Fonctions_QGIS.Reprojection(ends, SCR)

                if len(ends.selectedFeatures()) > 0:
                    Fonctions_QGIS.Selectbylocation(tempo, ends, True, 0)
                ends = tempo

            layer_data.addAttributes([QgsField("id", QVariant.String),QgsField("id_destination", QVariant.Int),QgsField("distance_km", QVariant.Double)])
            name = self.tr("Reseau_droites_etoile_depuis_{}")
            new_layer.setName(name.format(nodes.name()))
        
        # Defining the layer style
        style_path = os.path.dirname(os.path.abspath(__file__)).replace('SilLCP','Styles//Liens.qml')
        new_layer.loadNamedStyle(style_path)

        new_layer.updateFields()

        # Ajout de la couche à la carte
        QgsProject.instance().addMapLayer(new_layer)

        if self.full.isChecked():
            self.progress_dialog.setLabelText(self.tr("Préparation du réseau complet de lignes droites"))
            self.progress_dialog.setValue(0)
            QCoreApplication.processEvents()
            self.full_network(nodes, new_layer)
                
        if self.star.isChecked():
            self.progress_dialog.setLabelText(self.tr("Préparation du réseau en étoile de lignes droites"))
            self.progress_dialog.setValue(0)
            QCoreApplication.processEvents()
            self.star_network(nodes, ends, new_layer)
        
        self.progress_dialog.close()
        self.indicator_center.hide()
        self.indicator_center.setEnabled(False)
        self.indicator_center.setChecked(False)
        self.indicator_ends.hide()
        self.indicator_ends.setEnabled(False)
        self.indicator_ends.setChecked(False)

        self.check_nodes()


    def full_network(self, network, new_layer):
        """
        Creation of a complete network between all the nodes
        
        :param network: Layer of network nodes
        :type network: QgsVectorLayer

        :param new_layer: Layer created to host the network
        :type new_layer: QgsVectorLayer
        """
        layer_data = new_layer.dataProvider()
        list = []
        ref = []

        if self.indicator_center.isChecked():
            l = network.selectedFeatures()
        else:
            l = network.getFeatures()

        for node in l:
            list.append(node)
            ref.append(node)

        self.progress_dialog.setLabelText(self.tr("Nombre de points traités : 0/{}").format(len(list)))
        self.progress_dialog.setValue(0)
        QCoreApplication.processEvents()

        m = 0
        total = len(list)
        while len(list) != 1:
            start = list[0]
            id1 = ref.index(start)+1
            for end in list:
                id2 = ref.index(end)+1
                if end != start:
                    points = []
                    points.append(start.geometry().asPoint())
                    points.append(end.geometry().asPoint())
                    trace = QgsFeature()
                    trace.setGeometry(QgsGeometry.fromPolylineXY(points))
                    dist = trace.geometry().length()
                    layer_data.addFeature(trace)

                    if id1 < id2:
                        ids = str(id1) + "-" + str(id2)
                    else:
                        ids = str(id2) + "-" + str(id1)

                    with edit(new_layer):
                        for feat in new_layer.getFeatures():
                            attribute = feat.fields().names()
                            if feat[attribute[0]]==None:
                                new_layer.changeAttributeValue(feat.id(), 0, m)
                                new_layer.changeAttributeValue(feat.id(), 1, ids)
                                new_layer.changeAttributeValue(feat.id(), 2, id1)
                                new_layer.changeAttributeValue(feat.id(), 3, id2)
                                new_layer.changeAttributeValue(feat.id(), 4, dist/1000)
                    m += 1
            del list[0]
            self.progress_dialog.setLabelText(self.tr("Nombre de points traités : {}/{}").format(total-len(list), total))
            self.progress_dialog.setValue(round((total-len(list))*100/total))
            QCoreApplication.processEvents()    


    def star_network(self, network, ends, new_layer):
        """
        Creation of a star shaped network

        :param network: Layer of destination nodes
        :type network: QgsVectorLayer

        :param ends: Layer of central nodes
        :type ends: QgsVectorLayer

        :param new_layer: Layer created to host the network
        :type new_layer: QgsVectorLayer
        """
        layer_data = new_layer.dataProvider()
        listend = []
        list = []

        if self.indicator_ends.isChecked():
            l = ends.selectedFeatures()
        else:
            l = ends.getFeatures()

        for node in l:
            listend.append(node)

        if self.indicator_center.isChecked():
            l = network.selectedFeatures()
        else:
            l = network.getFeatures()

        for node in l:
            list.append(node)

        self.progress_dialog.setLabelText(self.tr("Nombre de points reliés : 0/{}").format(len(list)*len(listend)))
        self.progress_dialog.setValue(0)
        QCoreApplication.processEvents()

        m=0
        for center in list:
            for end in listend:
                points = []
                points.append(center.geometry().asPoint())
                points.append(end.geometry().asPoint())
                trace = QgsFeature()
                trace.setGeometry(QgsGeometry.fromPolylineXY(points))
                dist = trace.geometry().length()

                if dist != 0:
                    layer_data.addFeature(trace)
                    with edit(new_layer):
                            for feat in new_layer.getFeatures():
                                attribute=feat.fields().names()
                                if feat[attribute[0]] == None:
                                    new_layer.changeAttributeValue(feat.id(), 0, m)
                                    new_layer.changeAttributeValue(feat.id(), 1, center.id()+1)
                                    new_layer.changeAttributeValue(feat.id(), 2, dist/1000)
                    m += 1

                self.progress_dialog.setLabelText(self.tr("Nombre de points reliés : {}/{}").format(m, len(list)*len(listend)))
                self.progress_dialog.setValue(round(m*100/(len(list)*len(listend))))
                QCoreApplication.processEvents()

