"""
Fonctions intégrées à QGIS, reprises et appellées pour le fonctionnement du module
"""
import os

from qgis import *
from qgis.PyQt import *
from qgis.core import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from processing.tools import dataobjects
import processing
from .Utilitaires import *
import grassprovider

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

class Fonctions_QGIS(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, parent=None):
        """Constructor."""
        super(Fonctions_QGIS, 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)


    def Selectbylocation_simple(self, layer, intersect, method=0):
        """
        Input:  self
                layer (QgsVectorLayer) = Layer to be modified
                intersect (QgsVectorLayer) = Layer used for comparison
                method (int) 0 = New selection, 1 = Add to selection, 2 = Select from selection, 3 = Remove from selection
        Function: Selection by location of the entire layer
        """
        config = processing.ProcessingConfig
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 1)
        params = {'INPUT':layer,
                  'PREDICATE':[0],
                  'INTERSECT':intersect,
                  'METHOD':method}
        processing.run("native:selectbylocation", params)
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 2)

    
    def Selectbylocation(self, layer, intersect, selected=True, method=1):
        """
        Input:  self
                layer (QgsVectorLayer) = Layer to be modified
                intersect (QgsVectorLayer) = Layer used for comparison
                selected (bool) = Use only selected features
                method (int) 0 = New selection, 1 = Add to selection, 2 = Select from selection, 3 = Remove from selection
        Function: Selection by location using selected entities in a layer
        """
        config = processing.ProcessingConfig
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 1)
        params = {'INPUT':layer,
                  'PREDICATE':[0],
                  'INTERSECT':QgsProcessingFeatureSourceDefinition(intersect.source(), 
                                                                 selectedFeaturesOnly=selected, 
                                                                 featureLimit=-1, 
                                                                 geometryCheck=QgsFeatureRequest.GeometryNoCheck),
                  'METHOD':method}
        processing.run("native:selectbylocation", params)
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 2)

    
    def Selectbyattribute(self, layer, attribute, value, method=0):
        """
        Input:  self
                layer (QgsVectorLayer) = Layer to be modified
                attribute (str) = Reference attribute for selection
                value (int ou float) = Reference value for selection
                method (int) 0 = New selection, 1 = Add to selection, 2 = Remove from selection, 3 = Select from selection
        Function: Selection by attribute in a layer
        """
        config = processing.ProcessingConfig
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 1)
        params = {'INPUT': layer,
                'FIELD': attribute,
                'OPERATOR':0,
                'VALUE': value,
                'METHOD':method}
        processing.run("qgis:selectbyattribute", params)
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 2)

    
    def Buffer(self, layer, distance, selected=True, dissolve=True):
        """
        Input:  self
                layer (QgsVectorLayer) = Base layer for buffering/erosion
                distance (int) = Distance from buffer (in m)
                selected (bool) = Use only selected features
                dissolve (bool) = Merge buffers if contact
        Function: Creating a buffer around a point
        Output: Buffer/erosion created (QgsVectorLayer)
        """
        config = processing.ProcessingConfig
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 1)
        params = {'INPUT':QgsProcessingFeatureSourceDefinition(layer.source(), 
                                                              selectedFeaturesOnly=selected, 
                                                              featureLimit=-1, 
                                                              geometryCheck=QgsFeatureRequest.GeometryAbortOnInvalid),
                 'DISTANCE':distance,
                 'SEGMENTS':200,
                 'END_CAP_STYLE':0,
                 'JOIN_STYLE':0,
                 'MITER_LIMIT':2,
                 'DISSOLVE':dissolve,
                 'SEPARATE_DISJOINT':False,
                 'OUTPUT':'TEMPORARY_OUTPUT'}
        
        resulttemp = processing.run("native:buffer", params)
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 2)
        return resulttemp['OUTPUT']
    

    def Difference(self, min, max):
        """
        Input:  self
                min (int ou float) = Buffer with minimum distance
                max (int ou float) = Buffer with maximum distance
        Function: Create a ring between two distances min and max
        Output: Area resulting from the difference (QgsVectorLayer)
        """
        config = processing.ProcessingConfig
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 1)
        params={'INPUT':max,
                'OVERLAY':min,
                'OUTPUT':'TEMPORARY_OUTPUT',
                'GRID_SIZE':None}
        resulttemp = processing.run("native:difference", params)
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 2)
        return resulttemp['OUTPUT']
    

    def Aggregation(self, layer):
        """
        Input:  self
                layer (QgsVectorLayer) = Layer to be aggregated
        Function: Aggregation of entities in a layer according to a field (the parameters chosen here are linked to the module objectives)
        Output: Layer containing aggregated entities (QgsVectorLayer)
        """
        config = processing.ProcessingConfig
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 1)
        params = {'INPUT':layer,
                'GROUP_BY':'"ETAGE_FINAL"',
                'AGGREGATES':[{'aggregate': 'concatenate_unique','delimiter': ',','input': '"notation"','length': 4000,'name': 'notation','precision': 0,'sub_type': 0,'type': 10,'type_name': 'text'},
                              {'aggregate': 'concatenate_unique','delimiter': ',','input': '"descr"','length': 4000,'name': 'descr','precision': 0,'sub_type': 0,'type': 10,'type_name': 'text'},
                              {'aggregate': 'concatenate_unique','delimiter': ',','input': '"ere"','length': 4000,'name': 'ere','precision': 0,'sub_type': 0,'type': 10,'type_name': 'text'},
                              {'aggregate': 'concatenate_unique','delimiter': ',','input': '"serie"','length': 4000,'name': 'serie','precision': 0,'sub_type': 0,'type': 10,'type_name': 'text'},
                              {'aggregate': 'concatenate_unique','delimiter': ',','input': '"etage"','length': 4000,'name': 'etage','precision': 0,'sub_type': 0,'type': 10,'type_name': 'text'},
                              {'aggregate': 'concatenate_unique','delimiter': ',','input': '"type_gite"','length': 4000,'name': 'type_gite','precision': 0,'sub_type': 0,'type': 10,'type_name': 'text'},
                              {'aggregate': 'concatenate_unique','delimiter': ',','input': '"nom_num_carte"','length': 4000,'name': 'nom_num_carte','precision': 0,'sub_type': 0,'type': 10,'type_name': 'text'},
                              {'aggregate': 'concatenate_unique','delimiter': ',','input': '"etage_final"','length': 4000,'name': 'etage_final','precision': 0,'sub_type': 0,'type': 10,'type_name': 'text'}],
                'OUTPUT':'TEMPORARY_OUTPUT'}
        resulttemp = processing.run("native:aggregate", params)
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 2)
        return resulttemp['OUTPUT']
    

    def Aggregation_simple(self, layer):
        """
        Input:  self
                layer (QgsVectorLayer) = Layer to be aggregated
        Function: Merge the entities of a QgsVectorLayer, without taking into account the fields it contains in its attribute table
        Output: Layer where entities have been combined into a single entity (QgsVectorLayer)
        """
        config = processing.ProcessingConfig
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 1)
        params = {'INPUT':layer,
                'GROUP_BY':'NULL',
                'AGGREGATES':[],
                'OUTPUT':'TEMPORARY_OUTPUT'}
        resulttemp = processing.run("native:aggregate", params)
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 2)
        return resulttemp['OUTPUT']
    

    def Centroide(self, layer, selected=False):
        """
        Input:  self
                layer (QgsVectorLayer) = Layer of polygons used as a basis for centroids
        Function: Creating centroids within polygons
        Output: Layer containing centroids (QgsVectorLayer)
        """
        config = processing.ProcessingConfig
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 1)
        params={'INPUT':QgsProcessingFeatureSourceDefinition(layer.source(), 
                                                              selectedFeaturesOnly=selected, 
                                                              featureLimit=-1, 
                                                              geometryCheck=QgsFeatureRequest.GeometryAbortOnInvalid),
                'ALL_PARTS':False,
                'OUTPUT':'TEMPORARY_OUTPUT'}
        resulttemp = processing.run("native:centroids", params)
        config.setSettingValue('FILTER_INVALID_GEOMETRIES', 2)
        return resulttemp['OUTPUT']
    
    
    def VerticesExtraction(self, layer):
        """
        Input:  self
                layer (QgsVectorLayer) = Layer of polygons whose vertices will be extracted
        Function: Extraction of polygon vertices
        Output: Layer containing extracted vertices (QgsVectorLayer)
        """
        params = {'INPUT':layer,
                'OUTPUT':'TEMPORARY_OUTPUT'}
        resulttemp = processing.run("native:extractvertices", params)
        return resulttemp['OUTPUT']
    

    def Reprojection(self, layer, SCR):
        """
        Input:  self
                layer (QgsVectorLayer) = Vector layer to be reprojected
                SCR (int ou str) = SCR for reprojection
        Function: Vector layer reprojection tool
        Output: Reprojected layer (QgsVectorLayer)
        """
        params = {'INPUT':layer,
                'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:{}'.format(SCR)),
                'OUTPUT':'TEMPORARY_OUTPUT'}
        resulttemps = processing.run("native:reprojectlayer", params)
        return resulttemps['OUTPUT']


    def Centrality(self, layer):
        """
        Input:  self
                layer (QgsVectorLayer) = Network to be analysed
        Function: Network centrality analysis using the v.net.centrality tool
        Output: Layer of points containing centrality information (QgsVectorLayer)
        """
        try:
            print(grassprovider.Grass7Utils.Grass7Utils.installedVersion())
        except:
            self.v_GRASS = "grass"
        else:
            self.v_GRASS = "grass7"

        path = os.path.dirname(os.path.abspath(__file__))
        path = path.replace('Fonctions', 'Couches//T//Centralite.gpkg')

        params = {'input':layer,
                'degree':'degree',
                'closeness':'closeness',
                'betweenness':'betweenness',
                'eigenvector':'eigenvector',
                'iterations':1000,
                'error':0.1,
                'cats':'',
                'where':'',
                'arc_column':'',
                'arc_backward_column':'',
                'node_column':'',
                '-a':True,
                '-g':False,
                'output':path,
                'GRASS_REGION_PARAMETER':None,
                'GRASS_SNAP_TOLERANCE_PARAMETER':-1,
                'GRASS_MIN_AREA_PARAMETER':0.0001,
                'GRASS_OUTPUT_TYPE_PARAMETER':0,
                'GRASS_VECTOR_DSCO':'',
                'GRASS_VECTOR_LCO':'',
                'GRASS_VECTOR_EXPORT_NOCAT':False}
        resulttemps = processing.run(self.v_GRASS+":v.net.centrality", params)

        # La couche de sortie ne pouvant être utilisée que si elle est enregisrée, celle-ci est créée dans un des dossiers
        path2 = resulttemps['output']
        output_layer = QgsVectorLayer(path2,"Centralite",'ogr')

        # Création d'une couche temporaire pour la remplacer et faciliter son enregistrement par la suite
        SCR = output_layer.crs().authid().replace("EPSG:","")
        final_layer = Utilitaires.layer_creation(self, "Point", SCR, "Noeuds_reseau_centralite", output_layer)

        # QgsProject.instance().addMapLayer(output_layer)   
        output_layer.selectAll()

        layer_data = final_layer.dataProvider()
        layer_data.addFeatures(output_layer.selectedFeatures())
        # QgsProject.instance().removeMapLayer(output_layer)

        return final_layer
    

    def NodesExtraction(self, layer):
        """
        Input:  self
                layer (QgsVectorLayer) = Network to be analysed
        Function: Recovering network nodes using the v.net tool
        Output: Layer of points containing the network nodes (QgsVectorLayer)
        """
        try:
            print(grassprovider.Grass7Utils.Grass7Utils.installedVersion())
        except:
            self.v_GRASS = "grass"
        else:
            self.v_GRASS = "grass7"

        path = os.path.dirname(os.path.abspath(__file__))
        path = path.replace('Fonctions','Couches//T//Noeuds.gpkg')

        params = {'input':layer,
                'points':None,
                'file':'',
                'operation':0,
                'threshold':50,
                'arc_type':[0,1],
                '-s':False,
                '-c':False,
                'output':path,
                'GRASS_REGION_PARAMETER':None,
                'GRASS_SNAP_TOLERANCE_PARAMETER':-1,
                'GRASS_MIN_AREA_PARAMETER':0.0001,
                'GRASS_OUTPUT_TYPE_PARAMETER':0,
                'GRASS_VECTOR_DSCO':'',
                'GRASS_VECTOR_LCO':'',
                'GRASS_VECTOR_EXPORT_NOCAT':False}
        resulttemps = processing.run(self.v_GRASS+":v.net", params)

        # La couche de sortie ne pouvant être utilisée que si elle est enregisrée, celle-ci est créée dans un des dossiers
        path2 = resulttemps['output']
        output_layer = QgsVectorLayer(path2, "Noeuds", 'ogr')

        # Création d'une couche temporaire pour la remplacer et faciliter son enregistrement par la suite
        SCR = output_layer.crs().authid().replace("EPSG:","")
        final_layer = Utilitaires.layer_creation(self, "Point", SCR, "Noeuds", output_layer)

        # QgsProject.instance().addMapLayer(output_layer)   
        output_layer.selectAll()

        layer_data = final_layer.dataProvider()
        layer_data.addFeatures(output_layer.selectedFeatures())
        # QgsProject.instance().removeLayer(output_layer)

        return final_layer


    def PathExtraction(self, network, start, end):
        """
        Input:  self
                network (QgsVectorLayer) = Network to be explored
                start (str) = Starting point ('X,Y [EPSG:xxxx]')
                end (str) = Arrival point ('X,Y [EPSG:xxxx]')
        Function: Finding the shortest path in a network between two points
        Output: Shortest route identified (QgsVectorLayer)
        """
        params = {'INPUT':network,
                'STRATEGY':1,
                'DIRECTION_FIELD':'',
                'VALUE_FORWARD':'',
                'VALUE_BACKWARD':'',
                'VALUE_BOTH':'',
                'DEFAULT_DIRECTION':2,
                'SPEED_FIELD':'',
                'DEFAULT_SPEED':1.5,
                'TOLERANCE':0,
                'START_POINT':start,
                'END_POINT':end,
                'OUTPUT':'TEMPORARY_OUTPUT'}
        resulttemps = processing.run("native:shortestpathpointtopoint", params)

        return resulttemps['OUTPUT']
    

    def SelectEqual(self, layer, intersect, method):
        """
        Input:  self
                layer (QgsVectorLayer) = Layer to be modified
                intersect (QgsVectorLayer) = Layer used for comparison
                method (int) 0 = New selection, 1 = Add to selection, 2 = Select from selection, 3 = Remove from selection
        Function: Selection by location using selected entities in a layer to obtain entities with identical geometry
        """
        params = {'INPUT':layer,
                'PREDICATE':[3],
                'INTERSECT':intersect,
                'METHOD':method}
        processing.run("native:selectbylocation", params)


    def Extremities(self,layer):
        """
        Input:  self
                layer (QgsVectorLayer) = Layer from which the ends of entities must be selected
        Function: Recovering the first and last points of lines in a layer
        Sorties: first (QgsVectorLayer) = first point
                  last (QgsVectorLayer) = last point
        """
        params = {'INPUT':layer,
                 'VERTICES':'0',
                 'OUTPUT':'TEMPORARY_OUTPUT'}
        resulttemps = processing.run("native:extractspecificvertices", params)
        first = resulttemps['OUTPUT']

        params = {'INPUT':layer,
                 'VERTICES':'-1',
                 'OUTPUT':'TEMPORARY_OUTPUT'}
        resulttemps = processing.run("native:extractspecificvertices", params)
        last = resulttemps['OUTPUT']

        return first, last