# -*- coding: utf-8 -*-
"""
/***************************************************************************
 ErcTvbPluginDialog
                                 A QGIS plugin
 MitiConnect integrates ecological Connectivity in Mitigation Hierarchy
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                             -------------------
        begin                : 2021-08-25
        git sha              : $Format:%H$
        copyright            : (C) 2021 by INRAE
        email                : mathieu.chailloux@inrae.fr
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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, sys, shutil, time, numpy

import qgis
from qgis.PyQt import uic, QtWidgets
from qgis.PyQt.QtCore import Qt
from qgis.core import (
    Qgis,
    QgsProcessingContext,
    QgsProcessingUtils,
    QgsProcessingAlgRunnerTask,
    QgsApplication)

from ..qgis_lib_mc import utils
from ..qgis_lib_mc.utils import CustomException, joinPath
from ..qgis_lib_mc.abstract_model import DictItem, DictModel, TableToDialogConnector, CheckableComboDelegate
# from ..algs.erc_tvb_algs_provider import ErcTvbAlgorithmsProvider
from ..qgis_lib_mc.qgsTreatments import applyProcessingAlg
from ..qgis_lib_mc import qgsTreatments, qgsUtils, feedbacks, styles
from ..ui.scenario_dialog import ScenarioItem, ScenarioDialog, ScenarioLanduseDialog
from ..ui.plot_window import PlotWindow
from . import scenario

# Graphab utils

# nodata_val = 65535

def checkGraphabInstalled(feedback):
    return True
    # plugins = qgis.utils.available_plugins
    # installed = 'graphab4qgis' in plugins
    # if not installed:
        # feedback.user_error("Graphab is not installed")

#{ 'DIRPATH' : 'TEMPORARY_OUTPUT', 'INPUT' : 'D:/IRSTEA/ERC/tests/BousquetOrbExtended/Source/CorineLandCover/CLC12_BOUSQUET_ORB.tif', 'LANDCODE' : '241', 'NAMEPROJECT' : 'Project1', 'NODATA' : None, 'SIZEPATCHES' : 0 }

def getLinkset(gProj,linksetName):
    for linkset in gProj.project.costLinks:
        if linkset.name == linksetName:
            return linkset
    return None
def getGraph(gProj,graphName):
    for graph in gProj.project.graphs:
        if graph.name == graphName:
            return graph
    return None
    
PROVIDER = 'mitiConnect'
# TODO : grapha wrappers in erc_tvb_algs_provider ?
def createGraphabProject(landuse,codes,out_dir,project_name,
        con8=False,nodata=None,patch_size=0,feedback=None):
    code_str = ",".join([str(c) for c in codes])
    params = {
        'DIRPATH' : out_dir,
        'INPUT' : landuse,
        'LANDCODE' : code_str,
        'NAMEPROJECT' : project_name,
        'NODATA' : nodata,
        'SIZEPATCHES' : patch_size,
        'CON8' : con8 }
    return applyProcessingAlg(PROVIDER,'create_project',params,feedback=feedback)
def createGraphabLinkset(project,name,frictionPath,type=1,maxcost=None,feedback=None):
    params = { 'CODE' : '',
        'EXTCOST' : frictionPath,
        'INPUT' : project,
        'NAME' : name,
        'TYPE' : type }
    if maxcost:
        params['MAXCOST'] = maxcost
    return applyProcessingAlg(PROVIDER,'create_linkset',params,feedback=feedback)
def createGraphabGraph(project,linkset,unit=0,dist=0,graphName="",
        feedback=None):
    params = { 'DIST' : dist,
        'DISTUNIT' : unit,
        'INPUT' : project,
        'NAMEGRAPH' : graphName,
        'NAMELINKSET' : linkset }
    return applyProcessingAlg(PROVIDER,'create_graph',params,feedback=feedback)
def computeMetric(project,graphName,metricName=0,unit=0,
        d=1000,p=1,localMetric=True,feedback=None):
    params = { 'DISTUNIT' : unit,
        'DPARAMETER' : d,
        'GRAPHNAME' : graphName,
        'INPUT' : project,
        'METRICSNAME' : metricName,
        'PPARAMETER' : p }
    alg_name = 'local_metric' if localMetric else 'global_metric'
    return applyProcessingAlg(PROVIDER,alg_name,params,feedback=feedback)
def computeLocalMetric(project,graphName,metricName=0,unit=0,
        d=1000,p=0,feedback=None):
    return computeMetric(project,graphName,metricName=metricName,unit=unit,
        d=d,p=p,localMetric=True,feedback=feedback)
def computeGlobalMetric(project,graphName,metricName=0,unit=0,
        d=1000,p=0,feedback=None):
    return computeMetric(project,graphName,metricName=metricName,unit=unit,
        d=d,p=p,localMetric=False,feedback=feedback)
        
def getRegression(layer):
    distArr = [f["Dist"] for f in layer.getFeatures()]
    distMArr = [f["DistM"] for f in layer.getFeatures()]
    # res = numpy.polyfit(distArr,distMArr,1)
    res = numpy.polyfit(distMArr,distArr,1)
    utils.debug("distArr = {}".format(distArr))
    utils.debug("distMArr = {}".format(distMArr))
    utils.debug("res = {}".format(res))
    return res

class LaunchItem(DictItem):

    SCENARIO = 'SCENARIO'
    SPECIE = 'SPECIE'
    EXTENT = 'EXTENT'
    MAX_DISP = 'MAX_DISP'
    
    BASE_FIELDS = [ SCENARIO, SPECIE, EXTENT, MAX_DISP ]
    # DISPLAY_FIELDS = FIELDS
    
    def __init__(self,dict,pluginModel=None,feedback=None):
        super().__init__(dict,feedback=feedback)
        self.pluginModel = pluginModel
        self.paramRegr = None
        
    @classmethod
    def fromValues(cls,scName,spName,extent,maxDisp=None,pluginModel=None,
            fields=[],feedback=None):
        dict = { cls.SCENARIO : scName, cls.SPECIE : spName,
            cls.EXTENT : extent, cls.MAX_DISP : maxDisp }
        missingFields = [f for f in fields if f not in dict]
        for f in missingFields:
            dict[f] = None
        return cls(dict,pluginModel=pluginModel,feedback=feedback)
    @classmethod
    def fromDict(cls,dict,feedback=None):
        if cls.EXTENT not in dict:
            dict[cls.EXTENT] = None
        if cls.MAX_DISP not in dict:
            dict[cls.MAX_DISP] = None
        castDict = utils.castDict(dict)
        return cls(castDict,feedback=feedback)
    def getRegression(self):
        return self.paramRegr
    def setRegression(self,val):
        self.paramRegr = val
    def getScName(self):
        return self.dict[self.SCENARIO]
    def getSpName(self):
        return self.dict[self.SPECIE]
    def getExtName(self):
        return self.dict[self.EXTENT]
    def setExtName(self,val):
        self.dict[self.EXTENT] = val
    def getMaxDisp(self):
        return self.dict[self.MAX_DISP]
    def setMaxDisp(self,val):
        self.dict[self.MAX_DISP] = val
    def getScSpNames(self):
        return (self.getScName(), self.getSpName())
    def getNames(self):
        return (self.getScName(), self.getSpName(), self.getExtName())
        
    def equals(self,other):
        if other is None:
            return False
        selfNames = self.getNames()
        otherNames = other.getNames()
        return selfNames == otherNames
    def __eq__(self,other):
        return self.equals(other)

# Scenario
        
class LaunchModel(DictModel):

    def __init__(self, pluginModel):
        itemClass = getattr(sys.modules[__name__], LaunchItem.__name__)
        super().__init__(itemClass,
            feedback=pluginModel.feedback,
            fields=list(LaunchItem.BASE_FIELDS),
            display_fields=list(LaunchItem.BASE_FIELDS))
        # super().__init__(self,itemClass,feedback=pluginModel.feedback,
            # display_fields=ScenarioItem.DISPLAY_FIELDS)
        self.pluginModel = pluginModel
        
    # Add metric field if needed
    def addItem(self,item):
        for f in item.dict.keys():
            if f not in self.fields:
                self.addField(f)
        super().addItem(item)
    def mkItemFromDict(self,dict,feedback=None):
        feedback.pushDebugInfo("mkItemFromDict " + str(dict))
        for f in dict.keys():
            if f not in self.fields:
                self.addField(f)
        return self.itemClass.fromDict(dict,feedback=feedback)
        
    # Item getters
    def getScenarioNames(self):
        return [i.getName() for i in self.items]
    def getItemFromName(self,name):
        for i in self.items:
            if i.getName() == name:
                return i
        return None
    def getScItemFromName(self,scName):
        return self.pluginModel.scenarioModel.getItemFromName(scName)
    def getSpItemFromName(self,spName):
        return self.pluginModel.speciesModel.getItemFromName(spName)
    def getItems(self,item):
        scName, spName, extName = item.getNames()
        scItem = self.getScItemFromName(scName)
        spItem = self.getSpItemFromName(spName)
        extItem = self.getScItemFromName(extName)
        return (scItem, spItem, extItem)
    def getItemFromNames(self,scName,spName,extName=None):
        for i in self.items:
            iScName, iSpName, iExtName = i.getNames()
            if iScName == scName and iSpName == spName:
                if extName is None or extName == iExtName:
                    return i
        return None
    def scExists(self,name):
        i = self.getItemFromName(name)
        return (i is not None)
                 
    # Item base path getters
    def normPath(self,fname):
        return os.path.normcase(fname)
    def getItemNameSuffix(self,item,suffix):
        scName, spName, extName = item.getNames()
        if extName:
            suffix = "ext" + extName + "_" + suffix
        res = scName + "_" + spName + "_" + suffix
        return res
    def getScDirFromName(self,scName):
        scDir = self.getItemSpDir(scName)
        return self.normPath(scDir)
    def getItemExtentScDir(self,extName):
        dirname = "extent_" + str(extName)
        return self.pluginModel.getSubDir(dirname)
    def getItemExtentSpDir(self,item):
        scName, spName, extName = item.getNames()
        extentScDir = self.getItemExtentScDir(extName)
        spDir = self.pluginModel.getSubDir(spName,baseDir=extentScDir)
        return self.normPath(spDir)
    def getItemExtentPath(self,item):
        extentDir = self.getItemExtentSpDir(item)
        return self.normPath(joinPath(extentDir,"extent.shp"))
    def getItemBaseDir(self,item):
        scName = item.getScName()
        spDir = self.getItemExtentSpDir(item)
        scDir = self.pluginModel.getSubDir(scName,baseDir=spDir)
        return self.normPath(scDir)
    def getItemOutBase(self,item,suffix=""):
        spDir = self.getItemBaseDir(item)
        out_bname = self.getItemNameSuffix(item,suffix) + ".tif"
        return self.normPath(joinPath(spDir,out_bname))
    def getSpBaseLanduse(self,spItem):
        return self.pluginModel.speciesModel.getItemLandusePath(spItem)
        
    # Extent computing
    def computeItemExtent(self,item,eraseFlag=True,feedback=None):
        if feedback is None:
            feedback = self.feedback
        mf = feedbacks.ProgressMultiStepFeedback(2,feedback)
        mf.setCurrentStep(0)
        scName, spName, extName = item.getNames()
        scItem, spItem, extItem = self.getItems(item)
        self.feedback.pushDebugInfo("computeItemExtent " + str(extName))
        crs = self.pluginModel.paramsModel.getCrsStr()
        maxExtent = self.pluginModel.paramsModel.getExtentLayer()
        # Check out path
        out_path = self.getItemExtentPath(item)
        if utils.fileExists(out_path):
            if eraseFlag:
                qgsUtils.removeLayerFromPath(out_path)
            else:
                return out_path
        # Union of scenario and children
        scExtentLayers = self.pluginModel.scenarioModel.getItemExtentLayers(extItem)
        self.feedback.pushDebugInfo("scExtentLayers " + str(scExtentLayers))
        if not scExtentLayers:
            extPath = maxExtent
        else:
            mff = feedbacks.ProgressMultiStepFeedback(2,mf)
            mff.setCurrentStep(0)
            mergedPath = qgsUtils.mkTmpPath("extentsMerged.gpkg")
            qgsTreatments.mergeVectorLayers(scExtentLayers,crs,mergedPath,feedback=mff)
            extPath = qgsUtils.mkTmpPath("extentsMergedDissolved.gpkg")
            qgsTreatments.dissolveLayer(mergedPath,extPath,feedback=mff)
        mf.setCurrentStep(1)
        # Apply specie extent mode
        spLanduse = self.getSpBaseLanduse(spItem)
        if spItem.isMaxExtentMode() or extItem.isInitialState():
            self.feedback.pushDebugInfo("Copying %s to %s"%(extPath,out_path))
            extLayer = qgsUtils.loadVectorLayer(extPath)
            qgsUtils.writeShapefile(extLayer,out_path)
            # shutil.copy(extPath,out_path)
        elif spItem.isBufferMode():
            # bufferVal = float(spItem.getExtentVal
            bufferMulVal, maxDisp = float(spItem.getExtentVal()), int(spItem.getMaxDisp())
            if bufferMulVal == 0:
                self.feedback.user_error("Empty buffer for specie " + str(spName))
            if maxDisp == 0:
                self.feedback.user_error("Empty dispersal distance for specie " + str(spName))
            bufferVal = bufferMulVal * maxDisp
            extent = qgsTreatments.applyBufferFromExpr(extPath,
                bufferVal,out_path,feedback=mf)
        elif spItem.isCustomLayerMode():
            self.feedback.internal_error("Custom extent layer mode not implemented yet")
        else:
            self.feedback.internal_error("Unexpected specie mode " + str(spItem))
        mf.setCurrentStep(2)
        return out_path
        
    # Item getters for each step
    def getItemLanduse(self,item):
        return self.getItemOutBase(item,suffix="landuse")
    def getItemFriction(self,item):
        return self.getItemOutBase(item,suffix="friction")
    def getItemGraphabProjectName(self,item):
        return self.getItemNameSuffix(item,"graphab")
    def getItemGraphabProjectDir(self,item):
        spDir = self.getItemBaseDir(item)
        out_bname = self.getItemGraphabProjectName(item)
        return self.normPath(joinPath(spDir,out_bname))
    def getItemGraphabProjectFile(self,item):
        baseDir = self.getItemGraphabProjectDir(item)
        out_bname = self.getItemGraphabProjectName(item) + ".xml"
        return self.normPath(joinPath(baseDir,out_bname))
    def getItemLinksetName(self,item):
        return self.getItemNameSuffix(item,"linkset")
    def getItemGraphName(self,item):
        return self.getItemNameSuffix(item,"graph")
    def getItemDispersal(self,item):
        return self.getItemOutBase(item,suffix="dispersal")
    def getItemPatchFile(self,item):
        baseDir = self.getItemGraphabProjectDir(item)
        outBname = "patches.tif"
        return self.normPath(joinPath(baseDir,outBname))
    def getItemStartFile(self,item):
        return self.getItemOutBase(item,suffix="start")
        
    # Table flags
    def flags(self, index):
        return Qt.ItemIsSelectable | Qt.ItemIsEnabled
        
    def reload(self,eraseFlag=False):
        scModel = self.pluginModel.scenarioModel
        if eraseFlag:
            self.clearModel()
        itemsAdded = []
        for scItem in scModel.items:
            scName = scItem.getName()
            extentSc = scModel.getItemExtentSc(scItem)
            extName = extentSc.getName() if extentSc else extentSc
            for spItem in self.pluginModel.speciesModel.items:
                spName = spItem.getName()
                launchItem = LaunchItem.fromValues(scName,spName,extName,
                    pluginModel=self.pluginModel,fields=self.fields,
                    feedback=self.feedback)
                self.addItem(launchItem)
                itemsAdded.append(launchItem)
                if not scItem.isInitialState():
                    isScItem = scModel.getInitialState()
                    # Backward compatibility for old project files
                    if isScItem is None:
                        scModel.addInitialState()
                        isScItem = scModel.getInitialState()
                    isScName = isScItem.getName()
                    isItem = LaunchItem.fromValues(isScName,spName,extName,
                    pluginModel=self.pluginModel,fields=self.fields,
                    feedback=self.feedback)
                    self.addItem(isItem)
                    itemsAdded.append(isItem)
        if not eraseFlag:
            self.items = [i for i in self.items if i in itemsAdded]
        self.layoutChanged.emit()
    def reloadErase(self):
        self.reload(eraseFlag=True)
        
    def getItemRegression(self,item):
        linksetName = self.getItemLinksetName(item)
        layer = qgsUtils.getLoadedLayerByName(linksetName)
        if layer is None:
            msg = self.tr("Could not find layer for linkset ")
            msg += str(linksetName)
            msg += self.tr(", please ensure graphab project and linkset have been created for item ")
            msg += str(item)
            self.feedback.user_error(msg)
        scItem, spItem, extItem = self.getItems(item)
        res = getRegression(layer)
        return res
    def computeMaxDispCost(self,item,feedback):
        scName, spName, extName = item.getNames()
        scItem, spItem, extItem = self.getItems(item)
        # Retrieve IS item
        if scItem.isInitialState():
            isItem = item
        else:
            isSc = self.pluginModel.scenarioModel.getInitialState()
            isName = isSc.getName()
            isItem = self.getItemFromNames(isName,spName,extName)
        # Computes max disp
        spMaxDisp = spItem.getMaxDisp()
        if spItem.dispUnitIsMeters():
            isMaxDispCost = isItem.getMaxDisp()
            if isMaxDispCost is None:
                regr = self.getItemRegression(isItem)
                feedback.pushDebugInfo("paramRegr of %s equals to %s"%(isItem.getNames(),regr))
                if regr is None:
                    feedback.internal_error("No regression after computation for %s from %s"%(isItem,item))
                isA, isB = regr
                maxDispCost = float(isA * spMaxDisp + isB)
            else:
                maxDispCost = isMaxDispCost
        else:
            maxDispCost = spMaxDisp
        isItem.setMaxDisp(maxDispCost)
        item.setMaxDisp(maxDispCost)
        self.layoutChanged.emit()
        return maxDispCost
    def getMaxDispCost(self,item,feedback):
        self.feedback.pushDebugInfo("getMaxDispCost %s"%(item))
        scItem, spItem, extItem = self.getItems(item)
        maxDispCost = item.getMaxDisp()
        self.feedback.pushDebugInfo("maxDispCost %s"%(maxDispCost))
        if maxDispCost is None or not spItem.dispUnitIsMeters():
            maxDispCost = self.computeMaxDispCost(item,feedback)
        return maxDispCost
        
        
    def clearFile(self,filename):
        self.feedback.pushDebugInfo("clearFile " + str(filename))
        if utils.fileExists(filename):
            qgsUtils.removeLayerFromPath(filename)
            qgsUtils.removeRaster(filename)
    def clearStep(self,item,step=1):
        self.feedback.pushDebugInfo("clearStep " + str(step))
        if step <= 8:
            metricStr = self.pluginModel.paramsModel.getGlobalMetricStr()
            if metricStr in self.fields:
                item.dict[metricStr] = None
                self.layoutChanged.emit()
        if step <= 7:
            metricStr = self.pluginModel.paramsModel.getLocalMetricStr()
            if metricStr in self.fields:
                item.dict[metricStr] = None
                self.layoutChanged.emit()
        if step <= 4:
            dispPath = self.getItemDispersal(item)
            startPath = self.getItemStartFile(item)
            self.clearFile(dispPath)
            self.clearFile(startPath)
        projName = self.getItemGraphabProjectName(item)
        gProj = self.pluginModel.graphabPlugin.getProject(projName)
        if step <= 5:
            if gProj:
                graphName = self.getItemGraphName(item)
                #graph = getGraph(gProj,graphName)
                #if graph:
                #    qgsUtils.removeGroups(graphName)
                gProj.removeGraph(graphName)
            projectDir = self.getItemGraphabProjectDir(item)
            path_to_patchesshp = os.path.join(projectDir, "patches-topo.shp")
            self.clearFile(path_to_patchesshp)
            # patches_csv = os.path.join(projectDir, "patches.csv")
            # self.clearFile(patches_csv)
            self.feedback.pushDebugInfo("SETTING DISP TO NONE " + str(item))
            item.setMaxDisp(None)
            self.layoutChanged.emit()
        if step <= 4:
            if gProj:
                self.feedback.pushDebugInfo("proj")
                linksetName = self.getItemLinksetName(item)
                # linkset = getLinkset(gProj,linksetName)
                # if linkset:
                gProj.removeLinkset(linksetName)
        if step <=3:
            #project = self.getItemGraphabProjectFile(item)
            #if os.path.isfile(project):
            projName = self.getItemGraphabProjectName(item)
            qgsUtils.removeGroups(projName)
            projDir = self.getItemGraphabProjectDir(item)
            self.feedback.pushDebugInfo("Deleting " + str(projDir))
            shutil.rmtree(projDir,ignore_errors=True)
        if step <= 2:
            frPath = self.getItemFriction(item)
            self.clearFile(frPath)
        if step <= 1:
            luPath = self.getItemLanduse(item)
            self.clearFile(luPath)


        
        
    def applyItemLanduse(self,item,feedback=None,eraseFlag=False):
        if feedback is None:
            feedback = self.feedback
        scName, spName, extName = item.getNames()
        scItem, spItem, extItem = self.getItems(item)
        feedback.pushDebugInfo("applyItemLanduse " + str(scItem) + " " + str(spItem))
        mf = feedbacks.ProgressMultiStepFeedback(3,feedback)
        mf.setCurrentStep(0)
        # Check out path
        out_path = self.getItemLanduse(item)
        # Prepare Params
        spLanduse = self.getSpBaseLanduse(spItem)
        crs, maxExtent, resolution = self.pluginModel.getRasterParams()
        baseType, nodataVal = self.pluginModel.baseType, self.pluginModel.nodataVal
        # Main action
        if scItem.isInitialState() or scItem.isLanduseMode():
            luPath = spLanduse
        elif scItem.isStackedMode():
            feedback.pushDebugInfo("LU2")
            # Get base layer
            isSc = self.pluginModel.scenarioModel.getInitialState()
            isName = isSc.getName()
            isItem = self.getItemFromNames(isName,spName,extName)
            isLayer = self.getItemLanduse(isItem)
            if not os.path.isfile(isLayer):
                feedback.user_error("Landuse layer %s does not exist for %s"%(isLayer,isItem))
            # Apply landuse modifications
            scHierarchy = self.pluginModel.scenarioModel.getItemHierarchy(scItem)
            feedback.pushDebugInfo("scHierarchy = %s"%([s.getName() for s in scHierarchy]))
            nbSc, cpt = len(scHierarchy), 0
            scLayers = [isLayer]
            mff = feedbacks.ProgressMultiStepFeedback(nbSc,mf)
            mff.setCurrentStep(0)
            for cpt, sc in enumerate(reversed(scHierarchy),start=1):
                scLayer = self.pluginModel.scenarioModel.normalizeLayer(sc,feedback=mff)
                scLayers.append(scLayer)
                mff.setCurrentStep(cpt)
            # luPath = qgsUtils.mkTmpPath("%s_%s_%s_reclass.tif"%(scName,spName,extName))
            # Merge       
            luPath = qgsUtils.mkTmpPath(spName + "_landuse.tif")
            # qgsUtils.removeLayerFromPath(luPath)
            # qgsUtils.removeRaster(luPath)
            qgsTreatments.applyMergeRaster(scLayers,luPath,
                out_type=baseType,nodata_val=nodataVal,feedback=mf)
        # Extent
        mf.setCurrentStep(1)
        extentPath = self.getItemExtentPath(item)
        self.computeItemExtent(item,feedback=mf)
        mf.setCurrentStep(2)
        # Clip
        dst_crs = self.pluginModel.paramsModel.getCrsStr()
        qgsTreatments.clipRasterFromVector(luPath,extentPath,out_path,
            resolution=resolution,nodata=nodataVal,
            feedback=mf)
        mf.setCurrentStep(3)
        return out_path
        
    def getMatrixFromSpName(self,spName):
        frictionModel = self.pluginModel.frictionModel
        matrixes = frictionModel.getReclassifyMatrixes([spName])
        self.feedback.pushDebugInfo("matrixes = " + str(matrixes))
        matrix = matrixes[spName]
        self.feedback.pushDebugInfo("matrix = " + str(matrix))
        return matrix
    def getMatrixFromPath(self,spName,path):
        matrix = self.getMatrixFromSpName(spName)
        if not matrix:
            self.feedback.user_error("No friction value for specie {}".format(spName))
        # Get non assigned values
        inVals = qgsUtils.getRasterValsFromPath(path)
        mInVals, mOutVals = matrix[::3], matrix[2::3]
        self.feedback.pushDebugInfo("mInVals = " + str(mInVals))
        self.feedback.pushDebugInfo("mOutVals = " + str(mOutVals))
        naVals = [inV for inV, outV in zip(mInVals,mOutVals) if inV in inVals and outV == 0]
        self.feedback.pushWarning(self.tr("No friction value assigned to classes ") + str(naVals))
        return matrix
        
    def applyItemFriction(self,item,feedback=None,eraseFlag=False):
        if feedback is None:
            feedback = self.feedback
        feedback.pushDebugInfo("applyItemFriction")
        scName, spName, extName = item.getNames()
        scItem, spItem, extItem = self.getItems(item)
        baseType, nodataVal = self.pluginModel.baseType, self.pluginModel.nodataVal
        nodataVal = 65535
        # Check in path
        in_path = self.getItemLanduse(item)
        feedback.pushDebugInfo("in_path = " + str(in_path))
        if not utils.fileExists(in_path):
            self.feedback.user_error("No landuse file %s for specie %s in scenario %s"%(in_path,spName,scName))
        # Check out path
        out_path = self.getItemFriction(item)
        feedback.pushDebugInfo("out_path = " + str(out_path))
        # Apply friction reclassification
        if scItem.isInitialState():
            # Friction from tab
            if spItem.getFrictionMode():
                # Prepare matrix
                matrix = self.getMatrixFromPath(spName,in_path)
                # Call reclassify
                qgsTreatments.applyReclassifyByTable(in_path,matrix,out_path,
                    out_type=baseType,nodata_val=nodataVal,boundaries_mode=2,
                    feedback=feedback)
            else:
                # Friction from layer
                frictionLayer = spItem.getFrictionLayer()
                absFrictionLayer = self.pluginModel.paramsModel.getOrigPath(frictionLayer)
                extentPath = self.getItemExtentPath(item)
                self.pluginModel.paramsModel.normalizeRaster(absFrictionLayer,
                    extentLayerPath=extentPath,out_path=out_path,nodata_val=nodataVal,
                    feedback=feedback)
        else:
            # Stacked mode
            # Retrieve base scenario friction
            # baseScName = scItem.getBase()
            # baseScItem = self.pluginModel.scenarioModel.getItemFromName(baseScName)
            if spItem.getFrictionMode():
                # Friction from TAB
                matrix = self.getMatrixFromPath(spName,in_path)
                qgsTreatments.applyReclassifyByTable(in_path,matrix,out_path,
                    out_type=baseType,nodata_val=nodataVal,boundaries_mode=2,
                    feedback=feedback)
            else:
                # Friction from layer
                scHierarchy = self.pluginModel.scenarioModel.getItemHierarchy(scItem)
                nbSc = len(scHierarchy)
                mf = feedbacks.ProgressMultiStepFeedback(nbSc * 2 + 1,feedback)
                IS_name = self.pluginModel.scenarioModel.IS_NAME
                baseLaunchItem = self.getItemFromNames(IS_name,spName,extName)
                baseFriction = self.getItemFriction(baseLaunchItem)
                frictionLayers = [baseFriction]
                for cpt, sc in enumerate(reversed(scHierarchy),start=1):
                    scModifRaster = self.pluginModel.scenarioModel.normalizeLayer(sc,feedback=mf)
                    mf.setCurrentStep(cpt * 2 - 1)
                    matrix = self.getMatrixFromPath(spName,scModifRaster)
                    scModifFriction = qgsUtils.mkTmpPath("{}ModifFriction.tif".format(scName))
                    frictionLayer = qgsTreatments.applyReclassifyByTable(
                        scModifRaster,matrix,scModifFriction,
                        out_type=baseType,nodata_val=nodataVal,boundaries_mode=2,
                        feedback=mf)
                    frictionLayers.append(frictionLayer)
                    mf.setCurrentStep(cpt * 2)
                qgsTreatments.applyMergeRaster(frictionLayers,out_path,
                    out_type=baseType,nodata_val=nodataVal,feedback=mf)
                mf.setCurrentStep(nbSc * 2 + 1)
            
    #{ 'DIRPATH' : 'TEMPORARY_OUTPUT', 'INPUT' : 'D:/IRSTEA/ERC/tests/BousquetOrbExtended/Source/CorineLandCover/CLC12_BOUSQUET_ORB.tif', 'LANDCODE' : '241', 'NAMEPROJECT' : 'Project1', 'NODATA' : None, 'SIZEPATCHES' : 0 }
    def applyItemGraphabProject(self,item,feedback=None):
        if feedback is None:
            feedback = self.feedback
        feedback.pushDebugInfo("applyItemGraphabProject " + str(item))
        scName, spName, extName = item.getNames()
        scItem, spItem, extItem = self.getItems(item)
        checkGraphabInstalled(feedback)
        projName = self.getItemGraphabProjectName(item)
        landuse = self.getItemLanduse(item)
        friction = self.getItemFriction(item)
        if not utils.fileExists(landuse):
            feedback.user_error("No landuse file %s for specie %s in scenario %s"%(landuse,spName,scName))
        if not utils.fileExists(friction):
            feedback.user_error("No friction file %s for specie %s in scenario %s"%(friction,spName,scName))
        if spItem.isHabitatCodesMode():
            codes = spItem.getCodesVal()
        else:
            feedback.internal_error("Not yet implemented : habitat from layer")
        feedback.pushDebugInfo("codes = " + str(codes))
        if not codes:
            self.feedback.user_error("No habitat code specified for specie "
                + str(spName))
        minArea = spItem.getMinArea()
        patchConnexity = spItem.getPatchConnexity()
        con8 = not patchConnexity
        # Get outputs
        outDir = self.getItemBaseDir(item)
        feedback.pushDebugInfo("projName = " + str(projName))
        createGraphabProject(landuse,codes,outDir,projName,
            nodata=-self.pluginModel.nodataVal,patch_size=minArea,
            con8=con8,feedback=feedback)

    def applyItemGraphabLinkset(self,item,eraseFlag=False,feedback=None):
        if feedback is None:
            feedback = self.feedback
        feedback.pushDebugInfo("applyItemGraphabLinkset")
        scName, spName, extName = item.getNames()
        scItem, spItem, extItem = self.getItems(item)
        checkGraphabInstalled(feedback)
        projName = self.getItemGraphabProjectName(item)
        project = self.getItemGraphabProjectFile(item)
        linksetName = self.getItemLinksetName(item)
        friction = self.getItemFriction(item)
        self.pluginModel.loadProject(project)
        gProj = self.pluginModel.graphabPlugin.getProject(projName)
        if gProj:
            self.feedback.pushDebugInfo("gproj")
            self.feedback.pushDebugInfo("linkset = " + str(linksetName))
            self.feedback.pushDebugInfo("linksets = " + str([l.name for l in gProj.project.costLinks]))
            linkset = getLinkset(gProj,linksetName)
            if linkset:
                if eraseFlag:
                    self.clearStep(item,4)
                    # gProj.removeLinkset(linksetName)
                else:
                    linksetGroup = gProj.getLinksetGroup()
                    for layer in linksetGroup.children():
                        if layer.name() == linksetName:
                            layer.setItemVisibilityChecked(True)
                            return
                    # gProj.reloadLinksetCSV(linksetName)
        # Displays classes
        classes,array,nodata = qgsUtils.getRasterValsArrayND(friction)
        feedback.pushDebugInfo("classes = " + str(classes))
        feedback.pushDebugInfo("nodata = " + str(nodata))
        # Launches
        mf = feedbacks.ProgressMultiStepFeedback(2,feedback)
        flag, coeff = self.pluginModel.paramsModel.linksetMaxFlag, self.pluginModel.paramsModel.linksetMaxCoeff
        maxcost = None
        if flag:
            maxDispCost = spItem.getMaxDisp()
            maxcost = coeff * maxDispCost
            self.feedback.pushDebugInfo("maxcost = {}".format(maxcost))
        createGraphabLinkset(project,linksetName,friction,maxcost=maxcost,feedback=mf)
        mf.setCurrentStep(1)
        self.computeMaxDispCost(item,mf)
        mf.setCurrentStep(2)
        feedback.pushDebugInfo("Max disp cost of %s set to %s"%(item.getNames(),item.getMaxDisp()))
            
            
    def applyItemGraphabGraph(self,item,eraseFlag=False,feedback=None):
        if feedback is None:
            feedback = self.feedback
        feedback.pushDebugInfo("applyItemGraphabGraph")
        scName, spName, extName = item.getNames()
        scItem, spItem, extItem = self.getItems(item)
        checkGraphabInstalled(feedback)
        projName = self.getItemGraphabProjectName(item)
        project = self.getItemGraphabProjectFile(item)
        graphName = self.getItemGraphName(item)
        linksetName = self.getItemLinksetName(item)
        self.pluginModel.loadProject(project)
        gProj = self.pluginModel.graphabPlugin.getProject(projName)
        self.feedback.pushDebugInfo("gproj graph")
        if eraseFlag:
            self.clearStep(item,5)
        if gProj:
            feedback.pushDebugInfo("gProj")
            graph = getGraph(gProj,graphName)
            if graph:
                feedback.pushDebugInfo("graph")
                if eraseFlag:
                    pass
                    #self.clearStep(item,5)
                    # feedback.pushDebugInfo("erase")
                    # qgsUtils.removeGroups(graphName)
                    # gProj.removeGraph(graphName)
                    # assert(False)
                else:
                    graphsGroup = gProj.getGraphGroup()
                    graphsGroup.setItemVisibilityChecked(True)
                    for graphGroup in graphsGroup.children():
                        if graphGroup.name() == graphName:
                            graphGroup.setItemVisibilityChecked(True)
                    return
        # Retrieve regression values
        maxDispCost = self.getMaxDispCost(item,feedback)
        if maxDispCost <= 0:
            feedback.user_error("Incorrect dispersal distance {} (null or negative) for specie {}".format(maxDispCost,spName))
        # Build graph
        createGraphabGraph(project,linksetName,
            unit=1,dist=maxDispCost,graphName=graphName,feedback=feedback)        
            
    def checkGraph(self,proj,graphName):
        if proj is None:
            msg = self.tr("Could not find Graphab project for graph {}".format(graphName))
            self.feedback.user_error(msg)
        for graph in proj.project.graphs:
            if graph.name == graphName:
                return
        msg = self.tr("Could not find graph ")
        msg += graphName
        msg += self.tr(", please ensure step 5 has been launched before")
        self.feedback.user_error(msg)
            
    def computeDispersal(self,item,eraseFlag=False,feedback=None):
        if eraseFlag:
            self.clearStep(item,6)
        # Paths
        patchPath = self.getItemPatchFile(item)
        startPath = self.getItemStartFile(item)
        frictionPath = self.getItemFriction(item)
        outPath = self.getItemDispersal(item)
        # Compute dispersal
        if eraseFlag or not utils.fileExists(outPath):
            # Prepare start
            if not utils.fileExists(patchPath):
                feedback.user_error("No patch file %s for item %s"%(patchPath,item))
            qgsTreatments.applyRSetNull(patchPath,'0,-1',startPath,feedback=feedback)
            # Fetch dispersal capacity
            maxDispCost = self.getMaxDispCost(item,feedback)
            # Computes dispersal areas
            qgsTreatments.applyRCostFilterMaxCost(startPath,frictionPath,maxDispCost,outPath,feedback=feedback)
        # Load layers
        if not qgsUtils.isLayerLoaded(outPath):
            outLayer = qgsUtils.loadRasterLayer(outPath,loadProject=True)
            styles.setRandomColorRasterRenderer(outLayer)
                
    def computeLocalMetric(self,item,eraseFlag=False,feedback=None):
        if feedback is None:
            feedback = self.feedback
        scName, spName, extName = item.getNames()
        scItem, spItem, extItem = self.getItems(item)
        projName = self.getItemGraphabProjectName(item)
        project = self.getItemGraphabProjectFile(item)
        graphName = self.getItemGraphName(item)
        self.pluginModel.loadProject(project)
        gProj = self.pluginModel.graphabPlugin.getProject(projName)
        self.checkGraph(gProj,graphName)
        metricStr = self.pluginModel.paramsModel.getLocalMetricStr()
        l, g, d, p = self.pluginModel.paramsModel.getGraphabParams()
        self.feedback.pushDebugInfo("l = " + str(l))
        maxDispCost = self.getMaxDispCost(item,feedback)
        # Mode = max disp <=> p = 0.05
        dispMode = 1
        # Mode = 1 <=> max disp value expressed in cost unit
        dispUnit = 1
        # Compute metric value
        if eraseFlag or metricStr not in self.fields:
            val = computeLocalMetric(project,graphName,metricName=l,
                d=maxDispCost,unit=dispUnit,p=dispMode,feedback=feedback)
        else:
            val = item.dict[metricStr]
            if not val:
                val = computeLocalMetric(project,graphName,metricName=l,
                    d=maxDispCost,unit=dispUnit,p=dispMode,feedback=feedback)
        # Update table
        self.addField(metricStr)
        item.dict[metricStr] = val
        self.layoutChanged.emit()
        # Update nodes
        # projectDir = self.getItemGraphabProjectDir(item)
        # path_to_patchesshp = os.path.join(projectDir, "patches-topo.shp")
        # patch_layer = qgsUtils.getLayerByFilename(path_to_patchesshp)
        # patches_csv = os.path.join(projectDir, "patches.csv")
        # csv_layer = qgsUtils.loadLayer(patches_csv)
        # csv_layer.reload()
        # patch_layer.triggerRepaint()
        return val
                
    def computeGlobalMetric(self,item,eraseFlag=False,feedback=None):
        if feedback is None:
            feedback = self.feedback
        scName, spName = item.getScSpNames()
        projName = self.getItemGraphabProjectName(item)
        project = self.getItemGraphabProjectFile(item)
        gProj = self.pluginModel.graphabPlugin.getProject(projName)
        graphName = self.getItemGraphName(item)
        self.pluginModel.loadProject(project)
        self.checkGraph(gProj,graphName)
        l, g, d, p = self.pluginModel.paramsModel.getGraphabParams()
        self.feedback.pushDebugInfo("g = " + str(g))
        metricStr = self.pluginModel.paramsModel.getGlobalMetricStr()
        self.feedback.pushDebugInfo("metricStr = " + str(metricStr))
        # maxDispCost = self.getMaxDispCost(item,feedback)
        maxDispCost = self.getMaxDispCost(item,feedback)
        # Mode = max disp <=> p = 0.05
        dispMode = 1
        # Mode = 1 <=> max disp value expressed in cost unit
        dispUnit = 1
        # Check graph
        # projName = self.getItemGraphabProjectName(item)
        # gProj = self.pluginModel.graphabPlugin.getProject(projName)
        # if gProj:
            # graph = getGraph(gProj,graphName)
        # else:
            # pass
        # Compute metric value
        if eraseFlag or metricStr not in self.fields:
            val = computeGlobalMetric(project,graphName,metricName=g,
                d=maxDispCost,unit=dispUnit,p=dispMode,feedback=feedback)
        else:
            val = item.dict[metricStr]
            if not val:
                val = computeGlobalMetric(project,graphName,metricName=g,
                    d=maxDispCost,unit=dispUnit,p=dispMode,feedback=feedback)
        # Update table
        self.addField(metricStr)
        item.dict[metricStr] = val
        self.layoutChanged.emit()
        return val
        
    def removeItems(self,indexes):
        names = [self.items[ind.row()].getName() for ind in indexes]
        super().removeItems(indexes)
        self.pluginModel.removeImports(names)
        
    def updateFromXML(self,root,feedback=None):
        self.items = []
        super().updateFromXML(root,feedback=feedback)
        
    def clearModel(self):
        self.items = []
        self.fields = list(LaunchItem.BASE_FIELDS)

    # BASE_FIELDS = [ SCENARIO, SPECIE, EXTENT, MAX_DISP ]
    def getHeaderString(self,col):
        if col < 4:
            h = [self.tr('Scenario'),
                self.tr('Specie'),
                self.tr('Extent'),
                self.tr('Dispersal capacity')]
            return h[col]
        else:
            return self.fields[col]



class LaunchConnector(TableToDialogConnector):

    def __init__(self,dlg,model):
        self.dlg = dlg
        self.feedback = dlg.feedback
        super().__init__(model,self.dlg.launchesView)
        self.refreshScenarios()

    def refreshSpecies(self):   
        names = self.model.pluginModel.speciesModel.getNames()
        self.dlg.speciesSelection.clear()
        self.dlg.speciesSelection.insertItems(0,names)

    def refreshScenarios(self):   
        names = self.model.pluginModel.scenarioModel.getNames()
        self.dlg.scenariosSelection.clear()
        self.dlg.scenariosSelection.insertItems(0,names)

    def connectComponents(self):
        super().connectComponents()
        self.model.pluginModel.speciesModel.layoutChanged.connect(self.refreshSpecies)
        self.model.pluginModel.speciesModel.layoutChanged.connect(self.model.reload)
        self.model.pluginModel.scenarioModel.layoutChanged.connect(self.refreshScenarios)
        self.model.pluginModel.scenarioModel.layoutChanged.connect(self.model.reload)
        self.dlg.landuseRun.clicked.connect(self.landuseRun)
        self.dlg.frictionRun.clicked.connect(self.frictionRun)
        self.dlg.projectRun.clicked.connect(self.graphabProjectRun)
        self.dlg.linksetRun.clicked.connect(self.graphabLinksetRun)
        self.dlg.graphRun.clicked.connect(self.graphabGraphRun)
        self.dlg.dispersalRun.clicked.connect(self.computeDispersal)
        self.dlg.localMetricsRun.clicked.connect(self.computeLocalMetric)
        self.dlg.compareScenariosRun.clicked.connect(self.computeGlobalMetric)
        self.dlg.reloadButton.clicked.connect(self.model.reloadErase)
        # self.model.pluginModel.speciesModel.layoutChanged.connect(self.reload)
        # self.model.pluginModel.scenarioModel.layoutChanged.connect(self.reload)
        
    # def reload(self):
        # self.items = []
        # self.model.reload()
        # self.model.layoutChanged.emit()
        
    def getSelectedScenarios(self):
        scNames = self.dlg.scenariosSelection.checkedItems()
        self.feedback.pushDebugInfo("scNames = " + str(scNames))
        if not scNames:
            self.feedback.user_error("No scenario selected")
        items = [self.model.pluginModel.scenarioModel.getItemFromName(s) for s in scNames]
        self.feedback.pushDebugInfo("scNames 2 = " + str(len(items)))
        self.feedback.pushDebugInfo("scNames 3 = " + str(items))
        return items
        # indexes = self.view.selectedIndexes()
        # if not indexes:
            # self.feedback.user_error("No scenario selected")
        # rows = list(set([i.row() for i in indexes]))
        # res = [self.model.items[i] for i in rows]
        # return res
        
    def getSelectedSpecies(self):
        speciesNames = self.dlg.speciesSelection.checkedItems()
        self.feedback.pushDebugInfo("spNames = " + str(speciesNames))
        if not speciesNames:
            self.feedback.user_error("No specie selected")
        items = [self.model.pluginModel.speciesModel.getItemFromName(s) for s in speciesNames]
        return items
        
    def iterateRun(self,func):
        scenarios = self.getSelectedScenarios()
        species = self.getSelectedSpecies()
        nb_steps = len(scenarios) * len(species)
        step_feedback = feedbacks.ProgressMultiStepFeedback(nb_steps,self.feedback)
        cpt=0
        step_feedback.setCurrentStep(cpt)
        for sc in scenarios:
            self.feedback.pushDebugInfo("sc " + str(sc))
            for sp in species:
                func(sc,sp,feedback=step_feedback)
                cpt+=1
                step_feedback.setCurrentStep(cpt)
                
    def groupByExtent(self,scenarios):
        scModel = self.model.pluginModel.scenarioModel
        isSc = scModel.getInitialState()
        scMap = {}
        cpt = 0
        scenariosOrd = sorted(scenarios,key=lambda i: scModel.getItemDegree(i))
        for sc in scenariosOrd:
            baseSc = self.model.pluginModel.scenarioModel.getItemExtentSc(sc)
            self.feedback.pushDebugInfo("sc = " + str(sc.getName()))
            self.feedback.pushDebugInfo("scMap = " + str(scMap))
            if baseSc in scMap:
                self.feedback.pushDebugInfo("scMap(base) = " + str(scMap[baseSc]))
                self.feedback.pushDebugInfo("scMap(type) = " + str(scMap[baseSc].__class__.__name__))
                scMap[baseSc].append(sc)
            else:
                scMap[baseSc] = [sc]
            cpt += 1
        for baseSc, scList in scMap.items():
            sc0 = scList[0]
            if not sc0.isInitialState():
                scList.insert(0,isSc)
                self.feedback.pushDebugInfo("scenarios ordered with IS : " + str([sc.getName() for sc in scList]))
                cpt += 1
            # scMap[baseSc] = scenariosOrdered
        return (scMap, cpt)
        
    def iterateRunExtent(self,func):
        scenarios = self.getSelectedScenarios()
        species = self.getSelectedSpecies()
        eraseFlag = self.dlg.eraseResults.isChecked()
        # Build scMap
        scMap, nbSc = self.groupByExtent(scenarios)
        # nb steps feedback
        nb_steps = nbSc * len(species)
        self.feedback.pushDebugInfo("nbSc = {}".format(nbSc))
        self.feedback.pushDebugInfo("len(species) = {}".format(len(species)))
        self.feedback.pushDebugInfo("nb_steps = {}".format(nb_steps))
        step_feedback = feedbacks.ProgressMultiStepFeedback(nb_steps,self.feedback)
        step_feedback.setCurrentStep(0)
        cpt=0
        # Iteration
        for baseSc, scenarios in scMap.items():
            extName = baseSc.getName()
            for sp in species:
                spName = sp.getName()
                for sc in scenarios:
                    scName = sc.getName()
                    li = self.model.getItemFromNames(scName,spName,extName)
                    if li is None:
                        self.feedback.internal_error("No item found for "
                            + extName + " - " + spName + " - " + scName)
                    func(li,eraseFlag=eraseFlag,feedback=step_feedback)
                    cpt+=1
                    step_feedback.pushDebugInfo("Setting {} / {} step".format(cpt,nb_steps))
                    # step_feedback.pushDebugInfo("Nb steps = {}".format(step_feedback.mChildSteps))
                    step_feedback.setCurrentStep(cpt)
    
    def landuseItemRun(self,item,feedback=None,eraseFlag=None):
        out_path = self.model.getItemLanduse(item)
        loadResults = self.dlg.loadResults.isChecked()
        fileExists = utils.fileExists(out_path)
        if fileExists and eraseFlag:
            # Remove results
            self.model.clearStep(item,1)
        if eraseFlag or not fileExists:
            # Call landuse treatment
            self.model.applyItemLanduse(item,feedback=feedback)
        if loadResults:
            # Load layer in QGIS
            qgsUtils.loadRasterLayer(out_path,loadProject=loadResults,groupName="Landuse")
        return out_path
    def landuseRun(self):
        self.feedback.beginSection("Computing land use layer(s)")
        # self.iterateRunExtent(self.model.applyItemLanduse)
        self.iterateRunExtent(self.landuseItemRun)
        self.feedback.endSection()
        
    def checkJavaInstalled(self):
        provider = QgsApplication.processingRegistry().providerById('mitiConnect')
        java_cmd = provider.getJavaCommand()
        try:
            utils.checkCmd(java_cmd)
        except utils.UserError:
            msg = self.tr("Command ")
            msg += str(java_cmd)
            msg += self.tr(" does not exist, please install Java first")
            msg += " (https://www.java.com/en/download/)"
            raise utils.UserError(msg)
        
    def frictionItemRun(self,item,eraseFlag=None,feedback=None):
        out_path = self.model.getItemFriction(item)
        loadResults = self.dlg.loadResults.isChecked()
        fileExists = utils.fileExists(out_path)
        if fileExists and eraseFlag:
            # Remove results
            self.model.clearStep(item,2)
        if eraseFlag or not fileExists:
            # Call friction treatment
            self.model.applyItemFriction(item,feedback=feedback)
        if loadResults:
            # Load layer in QGIS
            loaded_layer = qgsUtils.loadRasterLayer(out_path,loadProject=loadResults,groupName="Friction")
            styles.setRendererPalettedGnYlRd(loaded_layer)
        return out_path
    def frictionRun(self):
        self.feedback.beginSection("Computing friction layer(s)")
        # params = {'INPUT' : 'D:/IRSTEA/ERC/tests/Simon2/wetransfer_perimetre_toulouse_metro_3km-dbf_2022-09-21_1435/UA_2018_L93.shp',
                   # 'OUTPUT' : 'D:/tmp/tmp.gpkg' }
        # context = QgsProcessingContext()
        # alg_name = 'native:dissolve'
        # alg = QgsApplication.processingRegistry().algorithmById(alg_name)
        # task = QgsProcessingAlgRunnerTask(alg, params, context, self.feedback)
        # QgsApplication.taskManager().addTask(task)
        self.iterateRunExtent(self.frictionItemRun)
        self.feedback.endSection()
    def graphabItemRun(self,item,eraseFlag=None,feedback=None):
        # loadResults = self.dlg.loadResults.isChecked()
        project = self.model.getItemGraphabProjectFile(item)
        fileExists = utils.fileExists(project)
        if eraseFlag:
            # Remove results
            self.model.clearStep(item,3)
        if eraseFlag or not fileExists:
            # Call graphab project
            self.model.applyItemGraphabProject(item,feedback=feedback)
        else:
            # Load project in QGIS
            feedback.pushInfo("Graphab file " + str(project) + " already exists")
            self.model.pluginModel.loadProject(project)
        return project
    def graphabProjectRun(self):
        self.feedback.beginSection("Creating Graphab project(s)")
        self.checkJavaInstalled()
        self.iterateRunExtent(self.graphabItemRun)
        self.feedback.endSection()
    def graphabLinksetRun(self):
        self.feedback.beginSection("Creating linkset(s)")
        self.checkJavaInstalled()
        self.iterateRunExtent(self.model.applyItemGraphabLinkset)
        self.feedback.endSection()
    def graphabGraphRun(self):
        self.feedback.beginSection("Creating graphs(s)")
        self.checkJavaInstalled()
        self.iterateRunExtent(self.model.applyItemGraphabGraph)
        self.feedback.endSection()
    def computeDispersal(self):
        self.feedback.beginSection("Computing dispersal areas(s)")
        self.iterateRunExtent(self.model.computeDispersal)
        self.feedback.endSection()
    def computeLocalMetric(self):
        self.feedback.beginSection("Computing local metric(s)")
        self.checkJavaInstalled()
        self.iterateRunExtent(self.model.computeLocalMetric)
        self.feedback.endSection()
    def computeGlobalMetric(self):
        self.feedback.beginSection("Computing global metric(s)")
        self.checkJavaInstalled()
        # Get UI state
        scenarios = self.getSelectedScenarios()
        species = self.getSelectedSpecies()
        cmpInit = self.dlg.cmpInit.isChecked()
        percentFlag = self.dlg.cmpPerc.isChecked()
        # Prepare feedback
        scMap, nbSc = self.groupByExtent(scenarios)
        nb_steps = nbSc * len(species)
        step_feedback = feedbacks.ProgressMultiStepFeedback(nb_steps,self.feedback)
        values = {}
        cpt = 0
        # Loop
        for baseSc, scenarios in scMap.items():
            extName = baseSc.getName()
            for sp in species:
                spName = sp.getName()
                initVal = -1
                for sc in scenarios:
                    scName = sc.getName()
                    item = self.model.getItemFromNames(scName,spName,extName)
                    val = self.model.computeGlobalMetric(item,feedback=step_feedback)
                    if sc.isInitialState():
                        initVal = val
                    elif initVal == -1:
                        assert(False)
                    elif initVal == 0:
                        self.feedback.internal_error("Empty value for global metric of initial state")
                    else:
                        if cmpInit:
                            if percentFlag:
                                finalVal = ((val - initVal) / initVal) * 100
                            else:
                                finalVal = val - initVal
                        else:
                            finalVal = val
                        if scName not in values:
                            values[scName] = {}
                        values[scName][spName] = finalVal
                    cpt += 1
                    step_feedback.setCurrentStep(cpt)
        self.feedback.endSection()
                # step_feedback.setCurrentStep(cpt)
        # Plot results in new window
        globalMetric = self.model.pluginModel.paramsModel.getGlobalMetricStr()
        window = PlotWindow(values,cmpInit,percentFlag,globalMetric,self.feedback)
        window.show()
        while window.exec_():
            pass


