# -*- coding: utf-8 -*-
"""
/***************************************************************************
 MitiConnect
                                 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

from qgis.PyQt import uic, QtWidgets
from qgis.PyQt.QtCore import Qt

from ..qgis_lib_mc.abstract_model import DictItem, ExtensiveTableModel, AbstractConnector
from ..qgis_lib_mc import utils, qgsUtils

NEW_VAL_STR = "New Value"

class TestItemDelegate(QtWidgets.QItemDelegate):

    def __init__(self):
        super().__init__()
        
    def createEditor(self, parent, option, index):
        lineedit=QtWidgets.QLineEdit(parent)
        return lineedit
        
# class CodesItemDelegate(QtWidgets.QItemDelegate):

    # def __init__(self,frictionModel):
        # super().__init__()
        # self.frictionModel = frictionModel
        
    # def createEditor(self, parent, option, index):
        # combo=QtWidgets.QComboBox(parent)
        # elements = self.frictionModel.getCodesStrComplete()
        # combo.insertItems(0,elements)
        # combo.insertItem(0,NEW_VAL_STR)
        # return combo
        
    # def setModelData(self, editor, model, index):
        # editorIndex=editor.currentIndex()
        # if editorIndex == 0:
            # newVal = self.frictionModel.getFreeVal()
        # else:
            # currIdx = editor.currentIndex()
            # currText = editor.currentText()
            # newVal = 
        # model.setData(index, text,role=Qt.EditRole)
        

class FrictionModel(ExtensiveTableModel):

    IMPORT = 'import'
    IMPORT_VAL = 'initVal'
    BASE_FIELDS = [ExtensiveTableModel.ROW_CODE, IMPORT, IMPORT_VAL, 
        ExtensiveTableModel.ROW_DESCR]

    def __init__(self,parentModel):
        self.parser_name = "FrictionModel"
        ExtensiveTableModel.__init__(self,parentModel,baseFields=self.BASE_FIELDS)
        self.feedback.pushInfo("FM1 " + str(self.__class__.__name__))
        self.feedback.pushInfo("FM2 " + str(self.itemClass.__class__.__name__))
        
    # @classmethod
    # def fromDict(cls,dict,feedback=None):
        # return cls(dict,feedback=feedback)
        
    # def mkItemFromXML(self,root,feedback=None):
        # d = dict(root.attrib)
        # d[ExtensiveTableModel.ROW_CODE] = int(d[ExtensiveTableModel.ROW_CODE])
        # return DictItem(d,feedback=feedback)
        
    def getMatchingItem(self,item):
        for i in self.items:
            importEquals = str(self.getItemImport(i)) == str(self.getItemImport(item))
            importValEquals = str(self.getItemImportVal(i)) == str(self.getItemImportVal(item))
            if importEquals and importValEquals:
                return i
        return None
        
    def reload(self):
        colNames = self.parentModel.speciesModel.getNames()
        
    def getItemImport(self,item):
        return item.dict[self.IMPORT]
    def getItemImportVal(self,item):
        return item.dict[self.IMPORT_VAL]
        
    def getFreeVals(self,nbVals):
        codes = [ self.getItemValue(i) for i in self.items ]
        freeVals = utils.getIntValues(nbVals,exclude_values=codes)
        return freeVals
    def getFreeVal(self):
        return self.getFreeVals(1)[0]
    def getInitVals(self,origin):
        vals = [self.getItemImportVal(i) for i in self.items if self.getItemImport(i) == origin]
        return vals
    def getCodes(self,origin=None):
        if origin:
            codes = [ self.getItemValue(i) for i in self.items if self.getItemImport(i) == origin ]
        else:
            codes = [ self.getItemValue(i) for i in self.items ]
        return codes
    def getItemStr(self,item):
        s = "{} - {} - {} - {}".format(
            item.dict[self.idField],
            item.dict[self.ROW_DESCR],
            item.dict[self.IMPORT],
            item.dict[self.IMPORT_VAL])
        return s
    def getCodesStr(self,withNewVal=False,origin=None,codes=[]):
        l = [NEW_VAL_STR] if withNewVal else []
        for i in self.items:
            if origin is None or origin == self.getItemImport(i):
                if codes == [] or i.dict[self.ROW_CODE] in codes:
                    s = self.getItemStr(i)
                    l.append(s)
        return l
        
    def addRowFromClassItem(self,item):
        d = { self.ROW_CODE : int(item.getNewVal()),
            self.IMPORT : item.getOrigin(),
            self.IMPORT_VAL : str(item.getInitVal()),
            self.ROW_DESCR : "" }
        rowItem = self.createRowFromDict(d)
        self.addRowItem(rowItem)
        self.layoutChanged.emit()
    # Called on class table update
    def updateFromClassItem(self,item):
        for cpt, i in enumerate(self.items):
            if self.getItemImport(i) == item.getOrigin() and self.getItemImportVal(i) == item.getInitVal():
                # i.dict[self.ROW_CODE] = int(item.getNewVal())
                newValStr = item.getNewVal()
                if newValStr.isdigit():
                    codes = set(self.getCodes())
                    newVal = int(newValStr)
                    if newVal == self.getItemValue(i):
                        i.dict[self.ROW_DESCR] = item.getDescription()
                        self.feedback.pushDebugInfo("friction item id updated")
                    else:
                        codes = set(self.getCodes())
                        if newVal in codes:
                            self.removeItemsFromRows([cpt])
                        else:
                            i.dict[self.ROW_CODE] = newVal
                            # i.dict[self.ROW_DESCR] = item.getDescription()
                            self.feedback.pushDebugInfo("friction item descr updated")
                else:
                    self.removeItemsFromRows([cpt])
                return
        self.addRowFromClassItem(item)
        
    def updateFromImports(self):
        codes = set(self.getCodes())
        self.feedback.pushDebugInfo("updateFromImports {}".format(codes))
        classCodes = set()
        toAdd = []
        # Add new values
        for i in self.parentModel.classModel.items:
            newValStr = i.getNewVal()
            if newValStr.isdigit():
                newVal = int(newValStr)
            else:
                continue
            classCodes.add(newVal)
            # self.feedback.pushDebugInfo("updateFromImports2 {}".format(newVal))
            if newVal in codes:
                continue
            toAdd.append(i)
        # Remove deleted values
        self.feedback.pushDebugInfo("classCodes {}".format(classCodes))
        toDelete = codes - classCodes
        self.feedback.pushDebugInfo("toDelete {}".format(toDelete))
        self.items = [i for i in self.items if self.getItemValue(i) not in toDelete]
        # Add values
        for i in toAdd:
            self.addRowFromClassItem(i)            
        self.layoutChanged.emit()
            
        
    # def addRowFromImport(self,values,name):
        # nbVals = len(values)
        # freeVals = self.getFreeVals(nbVals)
        # for oldVal, newVal in zip(values,freeVals):
            # d = { self.ROW_CODE : newVal,
                # self.IMPORT : name,
                # self.IMPORT_VAL : str(oldVal),
                # self.ROW_DESCR : "" }
            # rowItem = self.createRowFromDict(d)
            # self.addRowItem(rowItem)
        # self.layoutChanged.emit()
        
    def renameOrigin(self,oldName,newName):
        self.renameFieldValue(self.IMPORT,oldName,newName)
        
    # def getReclassTable(self,importName):
        # table = []
        # for i in self.items:
            # if self.getItemOrigin(i) == importName:
                # outVal = i.dict[self.idField]
                # inVal = i.dict[self.IMPORT_VAL]
                # table += [inVal,inVal,outVal]
        # return table
    # def getReclassDict(self,importName):
        # table = {}
        # for i in self.items:
            # if self.getItemOrigin(i) == importName:
                # outVal = i.dict[self.idField]
                # inVal = str(i.dict[self.IMPORT_VAL])
                # table[inVal] = outVal
        # return table
        
    def importExists(self,name):
        for i in self.items:
            if self.getItemImport(i) == name:
                return True
        return False
        
    def removeImports(self,importNames):
        self.feedback.pushDebugInfo("remmoveImports {}".format(self.items))
        self.items = [i for i in self.items if self.getItemImport(i) not in importNames]
        self.feedback.pushDebugInfo("remmoveImports2 {}".format(self.items))
        self.layoutChanged.emit()
    def reloadFriction(self,imports=None,species=None):
        # if imports is not None:
            # self.items = [i for i in self.items if i.getName() in import_names]
        self.updateFromImports()
        if species is not None:
            for i in self.items:
               pass
        
    # Updates friction model from scenario with parameters scName initVals and codes
    # def updateFromScenario(self,scName,initVals,codes):
        # self.feedback.pushDebugInfo("updateScenario " + str(scName))
        # self.feedback.pushDebugInfo("updateScenario " + str(initVals))
        # self.feedback.pushDebugInfo("updateScenario " + str(codes))
        # assert(len(initVals) == len(codes))
        # self.items = [i for i in self.items if self.getItemOrigin(i) != scName or self.getItemValue(i) in codes]
        # valuesToAdd = []
        # for initVal, code in zip(initVals,codes):
            # self.feedback.pushDebugInfo("updateScenario iterate " + str(code))
            # if code in self.getCodesStr():
                # self.feedback.pushDebugInfo("Ignoring existing code " + str(code))
            # else:
                # valuesToAdd.append(initVal)
        # self.feedback.pushDebugInfo("valuesToAdd " + str(valuesToAdd))
        # self.addRowFromImport(valuesToAdd,scName)
        
    def getHeaderString(self,col):
        if col < 4:
            h = [self.tr('New value'),
                self.tr('Origin'),
                self.tr('Init value'),
                self.tr('Description')]
            return h[col]
        else:
            return self.fields[col]
        
    def flags(self, index):
        baseFlags = Qt.ItemIsSelectable | Qt.ItemIsEnabled 
        if index.column() > 2:
            baseFlags = baseFlags | Qt.ItemIsEditable
        return baseFlags
        
        
    # used to init combo box in dialogs: find a best location for these functions ?
    # def initComboCodes(self,combo,val=None):
        # itemsStr = self.getCodesStrComplete(withNewVal=True)
        # combo.insertItems(0,itemsStr)
        # if val is None or val == "":
            # combo.setCurrentIndex(0)
        # else:
            # codes = self.getCodes()
            # idx = codes.index(val)
            # combo.setCurrentIndex(idx+1)
            
    # def getCodeFromCombo(self,combo):
        # idx = combo.currentIndex()
        # if idx == 0:
            # code = self.getFreeVals(1)[0]
        # else:
            # codes = self.getCodes()
            # code = codes[idx-1]
        # return code
        
        
          
class FrictionConnector(AbstractConnector):
    
    def __init__(self,dlg,frictionModel):
        self.dlg = dlg
        self.feedback = frictionModel.feedback
        super().__init__(frictionModel,self.dlg.frictionView)
                         #selectionCheckbox=self.dlg.frictRunOnlySelection)
        
    def initGui(self):
        pass
            
    def connectComponents(self):
        super().connectComponents()
        self.dlg.frictionView.setItemDelegate(TestItemDelegate())
        self.dlg.frictionLoadClass.clicked.connect(self.model.reloadFriction)
        # self.dlg.frictionRun.clicked.connect(self.applyItems)
        # self.dlg.classView.setModel(self.model)
        # self.model.layoutChanged.connect(self.hideSpeciesColumn)
        self.dlg.frictionSave.clicked.connect(self.saveCSVAction)
        self.dlg.frictionLoad.clicked.connect(self.loadCSVAction)
        
    # def hideSpeciesColumn(self):
        # nbCol = self.model.columnCount()
        # for n in range(4,nbCol):
            # self.dlg.classView.hideColumn(n)
        
    # Return indexes currently selected in friction view
    def getSelectedIndexes(self):
        if self.onlySelection:
            indexes = list(set([i.column() for i in self.view.selectedIndexes()]))
        else:
            indexes = range(3,len(self.model.fields))
        nb_indexes = len(indexes)
        if nb_indexes == 0:
            self.feedback.user_error("No species selected for friction step")
        nbCols = len(self.model.getExtFields())
        self.feedback.pushDebugInfo("nbCols = " + str(nbCols))
        for idx in indexes:
            st_idx = idx - 3
            if st_idx < 0 or st_idx >= nbCols:
                self.feedback.user_error("Column " + str(idx) + " selected is not a specie")
        return indexes
        
    # Updates model with items loaded from file 'fname'
    def loadCSV(self,fname):
        utils.checkFileExists(fname)
        self.model.fromCSVUpdateExisting(fname)
        self.feedback.pushInfo("Friction loaded from '" + str(fname))
        
    # Opens file dialog and loads model from selected CSV file.
    def loadCSVAction(self):
        self.feedback.pushDebugInfo("loadCSVAction " + str(self))
        fname = qgsUtils.openFileDialog(parent=self.dlg,
                                      msg=self.tr("Open CSV file"),
                                      filter="*.csv")
        if fname:
            self.loadCSV(fname)
            
    def saveCSV(self,fname):
        self.model.saveCSV(fname)
     
    def saveCSVAction(self):
        self.feedback.pushDebugInfo("saveCSVAction")
        fname = qgsUtils.saveFileDialog(parent=self.dlg,
                                      msg="Save friction as CSV file",
                                      filter="*.csv")
        if fname:
            self.saveCSV(fname)
        