from __future__ import absolute_import
from builtins import str
from builtins import range
# !/usr/bin/python
#-*- coding: latin-1 -*-
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui
from qgis.core import *
from qgis.gui import *
from .Ui_RUSLE_soil import Ui_RUSLE_soil
from . import GdalTools_utils as Utils
from osgeo import gdal
import os
import osgeo
try:
    from PyQt5.QtCore import QString
except ImportError:
    QString = str
from . import GdalTools_utils as Utils
from . import ftools_utils
from processing.core.Processing import Processing

#diretory_home = os.path.dirname(os.path.realpath(__file__))
SYS_DIR  = "C:/OSGeo4W64/apps"
QGIS_DIR = os.path.join(SYS_DIR, 'qgis')

class RUSLE_soil(QDialog, Ui_RUSLE_soil):
    
    def __init__(self, iface):
        QDialog.__init__(self)
        self.iface = iface
        self.setupUi(self)
        
        self.selectButton_soil.clicked.connect(self.fillInputFileEdit)
        self.selectButton_out.clicked.connect( self.fillOutputFileEdit)
        self.buttonAdd.clicked.connect( self.actionAdd)
        self.buttonRemove.clicked.connect( self.actionRemove)
        self.buttonBox.accepted.connect( self.convert)         
        self.inputLayerCombo_soil.currentIndexChanged.connect(self.fillInputAttrib)
        self.buttonPDF.clicked.connect( self.pdf)
        self.buttonAttribute.clicked.connect( self.attributeTable)   
        
        # connect help
        #self.buttonBox.button(QtGui.QDialogButtonBox.Help).clicked.connect( self.help)
        
    # HELP
    def help(self):
       
        QMessageBox.about(self, "RUSLE", """<p><b>K factor</b></p> 
        <p><b>Definition</b>: Soil erodibility factor represents the susceptibility of a soil type to erosion per unit erosivity of rainfall, i.e., 
        it is the reciprocal of soil resistance to erosion (Wischmeier and Smith, 1978). This factor is related to soil texture, organic matter
        content, permeability class (Renard et al., 1997) and could be estimated using the algorithm proposed by Wischmeier et al. (1971): K=  (2.1 � (10)^
        (-4) (12-OM)�M^1.14+3.25(s-2)+2.5(p-3))/100, where OM is the organic matter (%), s is soil structure class, p is permeability class and M is aggregated 
        variable derived from the granular soil texture: M=(%Msilt)�(%silt+%sand) and the modified silt (Msilt) is a percentage of grain size between 0.002 and 0.1 mm.
        The K factor is related to soil type, soil structure and permeability, grain size, drainage potential, among other factors. Regarding this, the input field is,
        usually, the soil map shapefile. According to Pimenta (1998), the procedure is simplified creating Portugal tables due to the dispersed, varied and heterogeneous
        data required to compute this. The tables are composed by the erodibility values according to each soil type and family based on SROA (Servi�o de Reconhecimento e Ordenamento Agr�rio).</p>
        <p><b>Method</b></p> 
        <p>Input file = Soil map. </p>
        <p>Parameters = Attribute with the soil occupation and the cell size. Two tables (1 and 2) with the classification (parameters or by soil type) 
        and the K factor. Please attempt to have the attribute as string type. </p>
        <p>Output file = K factor raster file </p>
        <p>Wischmeier, W.H., Smith, D.D., 1978. Predicting Rainfall Erosion Losses: A Guide to Con-servation Planning with Universal Soil Loss Equation (USLE). Agriculture Handbook, Department of Agriculture, 
        Washington, DC, 703</p>""")          

# INPUT VECTOR FILE
    def fillInputFileEdit(self):
        lastUsedFilter = Utils.FileFilter.lastUsedVectorFilter()
        inputFile, encoding = Utils.FileDialog.getOpenFileName(self, self.tr( "Select the input file" ), Utils.FileFilter.allVectorsFilter(), lastUsedFilter, True)
        self.lastEncoding = encoding
        self.loadFields(inputFile)
        self.inputLayerCombo_soil.addItem(inputFile)
        check = QFile(inputFile) 
        
    def fillInputAttrib(self, inputFile):
        self.layer = QgsVectorLayer(self.inputLayerCombo_soil.currentText(), 'inputFile'  , "ogr")
        self.lineAttrib.clear()    
        changedField = ftools_utils.getFieldList(self.layer)
        for f in changedField:
            if f.type() == QVariant.Int or f.type() == QVariant.String or f.type()==QVariant.Double:
                self.lineAttrib.addItem(str(f.name()))   
                
    def loadFields(self, vectorFile = QString()):
        if vectorFile == None:
            return
        try:
            (fields, names) = Utils.getVectorFields(vectorFile)
        except Exception as e:
            QErrorMessage(self).showMessage( str(e) )          
        self.inputLayerCombo_soil.clearEditText()
        ncodec = QTextCodec.codecForName(self.lastEncoding)  
        
# ------------------------------ // ------------------------------------ // ----------------------------
       
   # OUTPUT RASTER FILE
    def fillOutputFileEdit(self):
        lastUsedFilter = Utils.FileFilter.lastUsedRasterFilter()
        outputFile = Utils.FileDialog.getSaveFileName(self, self.tr( "Select the raster file to save the results to" ), Utils.FileFilter.allRastersFilter(), lastUsedFilter )
        self.inputLayerCombo_out.setText(outputFile) 
        
# ---------------------------------------------------------------------------------------------------------

# BUTTON ADD AND REMOVE CLASSES
    def actionAdd(self):
        n = self.tableWidget.rowCount()
        self.tableWidget.insertRow(n)
        n = self.tableWidget.rowCount()
        return True
    
    def actionAdd_k(self):
        n = self.tableWidget_k.rowCount()
        self.tableWidget_k.insertRow(n)
        n = self.tableWidget_k.rowCount()
        return True    
              
    def actionRemove(self):
        n = self.tableWidget.rowCount()
        for i in range(1,n):
            self.tableWidget.removeRow(n-1)
        n = self.tableWidget.rowCount()
        return True
    
    def actionRemove_k(self):
        n = self.tableWidget_k.rowCount()
        for i in range(1,n):
            self.tableWidget_k.removeRow(n-1)
        n = self.tableWidget_k.rowCount()
        return True    
# -------------------------------------------------------------------------------------------------------------

# PDF CONNECTION

    def pdf(self):
        ## access to pdf files 
        import subprocess
        pdf = os.path.join(QGIS_DIR, "python/plugins/RUSLE/Table K Factor.pdf")
        subprocess.Popen([pdf],shell=True)  
        
# ------------------------------------------------------------------------------------------------------------

    def attributeTable(self):
        inputLayer = self.inputLayerCombo_soil.currentText()
        # layer information
        layer = QgsVectorLayer(inputLayer, 'inputLayer' , "ogr")
        # attribute
        Elevation = self.lineAttrib.currentText()        
        fields = layer.fields()
        field_names = [field.name() for field in fields]
        n = len(field_names)
        lista_attrib = []
        for i in range(0,n):
            f = field_names[i]
            if f==str(Elevation):
                number = i
                for feat in layer.getFeatures():
                    attrb = feat.attributes()
                    attribute_read = attrb[number] #reads the attribute one by one
                    lista_attrib = lista_attrib + [str(attribute_read)]
        # list of description on attribute table of shapefile
        lista_attributes = lista_attrib   
        len_attb = len(lista_attributes)
       
        # delete duplicate names
        lista_att_dupl = []
        [lista_att_dupl.append(i) for i in lista_attributes if not i in lista_att_dupl]        
                
        
        # if the user select table 1
        if self.checkBox1.isChecked():
            # save the description of shapefile in tool table and delete all the other information, such as indexes and other descriptions
            numberRows = int(self.tableWidget.rowCount())
            self.tableWidget.clearContents()
            
            for j in range(0,len(lista_att_dupl)):
                self.tableWidget.setItem(j, 0, QTableWidgetItem(lista_att_dupl[j]))
                
            # remove the lines with no values
            for a in range(0, numberRows):
                if self.tableWidget.item(a,0)== None:
                    self.actionRemove()
        
        # if the user select table 2 
        if self.checkBox2.isChecked():
            # save the description of shapefile in tool table and delete all the other information, such as indexes and other descriptions
            numberRows = int(self.tableWidget_k.rowCount())
            self.tableWidget_k.clearContents()
            
            for j in range(0,len(lista_att_dupl)):
                self.tableWidget_k.setItem(j, 0, QTableWidgetItem(lista_att_dupl[j]))
                
            # remove the lines with no values
            for a in range(0, numberRows):
                if self.tableWidget_k.item(a,0)==None:
                    self.actionRemove_k()  
                    
        # if the user doesn't select any table
        else:
            QMessageBox.warning(self, "RUSLE",
                                   "Please select a table.",
                                   QMessageBox.Ok);        

# ------------------------------------------------------------------------------------------------------------
        
# CONVERT SHAPEFILE TO RASTER
    def convert(self):    
        self.buttonBox.button(QDialogButtonBox.Ok).setDefault(False)
        if self.checkBox1.isChecked():
            inputLayer = self.inputLayerCombo_soil.currentText()
            # layer information
            layer = QgsVectorLayer(inputLayer, 'inputLayer' , "ogr")
            vectorlayer_vector =  layer.dataProvider()
            # extent
            extent_rect = vectorlayer_vector.extent()
            xmin = extent_rect.xMinimum()
            xmax = extent_rect.xMaximum()
            ymin = extent_rect.yMinimum()
            ymax = extent_rect.yMaximum()
            extent = str(xmin) + "," + str(xmax) + "," + str(ymin) + "," + str(ymax)
            # attribute
            Elevation = self.lineAttrib.currentText()
            # cellsize
            cellSize = int(self.linePix.value())
            outPath = self.inputLayerCombo_out.text() 
            # read fields and add a new column with the factor C
            fields = layer.fields()
            new_field = QgsField("Fator_K", QVariant.Double)
            layer_new = vectorlayer_vector.addAttributes([new_field])
            layer.updateFields()
            newFieldIndex = vectorlayer_vector.fieldNameIndex(new_field.name())
            allAttrs = vectorlayer_vector.attributeIndexes()
            # editing the new column
            numberRows = int(self.tableWidget.rowCount())
            numberColumns = int(self.tableWidget.columnCount())
            classes = ''
            lista = []
            for i in range(0,numberRows):
                for j in range(0,numberColumns):
                    self.line = self.tableWidget.item(i,j)
                    lista = lista + [str(self.line.text())]      
            
            # list of description on tool table
            lista_table = lista
           
            field_names = [field.name() for field in fields]
            n = len(field_names)
            lista_attrib = []
            for i in range(0,n):
                f = field_names[i]
                if f==str(Elevation):
                    number = i
                    for feat in layer.getFeatures():
                        attrb = feat.attributes()
                        attribute_read = attrb[number]
                        lista_attrib = lista_attrib + [str(attribute_read)]
            # list of description on attribute table of shapefile
            lista_attributes = lista_attrib           
           
            # permeability values list
            list_perm = []
            for i in range(0, numberRows):
                self.perm = self.tableWidget.item(i,1)
                list_perm = list_perm + [str(self.perm.text())]
            
            # structure values list
            list_struct = []
            for i in range(0, numberRows):
                self.struct = self.tableWidget.item(i,2)
                list_struct = list_struct + [str(self.struct.text())]  
              
            # clay percentage list
            list_clay = []
            for i in range(0, numberRows):
                self.clay = self.tableWidget.item(i,3)
                list_clay = list_clay + [str(self.clay.text())]   
           
            # silt and very fine sand list
            list_silt = []
            for i in range(0, numberRows):
                self.silt = self.tableWidget.item(i,4)
                list_silt = list_silt + [str(self.silt.text())]
           
            # gravel percentage list
            list_gravel = []
            for i in range(0, numberRows):
                self.gravel = self.tableWidget.item(i,5)
                list_gravel = list_gravel + [str(self.gravel.text())]
              
            # MO percentage list
            list_org = []
            for i in range(0, numberRows):
                self.org = self.tableWidget.item(i,6)
                list_org = list_org + [str(self.org.text())]
            
            len_items = len(list_org)
            
            # K factor, erodibility factor
            list_K_values = []
            for c in range(0, len_items):
                # clay percentage calculated
                clay = 100-float(list_silt[c])-float(list_gravel[c])

                # particle size parameter
                M = float(list_silt[c]) * (100-clay)
               
                # K fator SI
                K = (2.1 * 10**(-4) * (12 - float(list_org[c])) * M**(1.14) + 3.25 * (float(list_struct[c])-2) + 2.5 * (float(list_perm[c]) - 3)) / (100 * 7.59)

                # K fator metrics (* 9.8 ms^2)
                K_metrics = K * 9.8
                list_K_values = list_K_values + [K_metrics]
          
            # without repetitions
            description_common = set(lista_attributes).intersection(lista_table)
            listDescription = list(description_common)
           
            # list of soil descriptions
            list_soil = []
            for i in range(0, numberRows):
                self.soil = self.tableWidget.item(i,0)
                list_soil = list_soil + [str(self.soil.text())]    
           
            # assign the K values to each description in attribute table
            listElements = []
            listElem = []
            for j in range(0,len(listDescription)):
                elem = lista_table.index(listDescription[j])
                listElements = listElements + [elem] 
                elem_index = lista_table[int(elem+1)]
                listElem = listElem + [float(elem_index)]
           
            for l in range(0, len(listElem)):
                layer.startEditing()
                exp = QgsExpression(str(listElem[l]))
                exp.prepare(fields)   
                elemDescription = lista_table[listElements[l]]
                for f in layer.getFeatures():
                    # get attributes of column defined by the user
                    attrb_elem = f[number]
                    if attrb_elem==elemDescription: 
                        f[newFieldIndex] = exp.evaluate(f)
                        layer.updateFeature(f)  
                layer.commitChanges()   
            list_attrb_newField = []
            for features in layer.getFeatures():
                attrb_newField = features.attributes()
                attrb_newField_read = attrb_newField[number+1]
               
            # update and read the new field
            fieldsNew = layer.fields()
            field_names_new = [newField.name() for newField in fieldsNew]        
            parameter_indexes = field_names_new[newFieldIndex]   
           
            Processing.initialize()
            #Processing.runAlgorithm("grass:v.to.rast.attribute", None, inputLayer, 0, parameter_indexes, extent, cellSize, -1.0, 0.0001, outPath)
            Processing.runAlgorithm("grass7:v.to.rast", {
                'input': inputLayer,
                'type': [0, 1, 3], 'where': '', 'use': 0, 'attribute_column': parameter_indexes, 'rgb_column': None,
                'label_column': None, 'value': 1, 'memory': 300, 'output': outPath,
                'GRASS_REGION_PARAMETER': None, 'GRASS_REGION_CELLSIZE_PARAMETER': cellSize,
                'GRASS_RASTER_FORMAT_OPT': '',
                'GRASS_RASTER_FORMAT_META': '', 'GRASS_SNAP_TOLERANCE_PARAMETER': -1,
                'GRASS_MIN_AREA_PARAMETER': 0.0001})

            # add result into canvas
            file_info = QFileInfo(outPath)
            if file_info.exists():
                layer_name = file_info.baseName()
            else:
                return False
            rlayer_new = QgsRasterLayer(outPath, layer_name)
            # add result into canvas
            QgsProject.instance().addMapLayer(rlayer_new)
            self.canvas = QgsMapCanvas()
            # self.canvas.scene().addItem(vdlayer)
            QgsProject.instance().addMapLayer(rlayer_new)
            QMessageBox.information(self, self.tr( "Finished" ), self.tr( "RUSLE Factor K completed." ) )
            
        if self.checkBox2.isChecked():
            inputLayer = self.inputLayerCombo_soil.currentText()
            # layer information
            layer = QgsVectorLayer(inputLayer, inputLayer , "ogr")
            vectorlayer_vector =  layer.dataProvider()
            # extent
            extent_rect = vectorlayer_vector.extent()
            xmin = extent_rect.xMinimum()
            xmax = extent_rect.xMaximum()
            ymin = extent_rect.yMinimum()
            ymax = extent_rect.yMaximum()
            extent = str(xmin) + "," + str(xmax) + "," + str(ymin) + "," + str(ymax)
            # attribute
            Elevation = self.lineAttrib.currentText()
            # cellsize
            cellSize = int(self.linePix.value())
            outPath = self.inputLayerCombo_out.text() 
            # read fields and add a new column with the factor C
            fields = layer.fields()
            new_field = QgsField("Fator_K", QVariant.Double)
            layer_new = vectorlayer_vector.addAttributes([new_field])
            layer.updateFields()
            newFieldIndex = vectorlayer_vector.fieldNameIndex(new_field.name())
            allAttrs = vectorlayer_vector.attributeIndexes()
            # editing the new column
            numberRows = int(self.tableWidget_k.rowCount())
            numberColumns = int(self.tableWidget_k.columnCount())
            classes = ''
            lista = []
            for i in range(0,numberRows):
                for j in range(0,numberColumns):
                    self.line = self.tableWidget_k.item(i,j)
                    lista = lista + [str(self.line.text())]
           
            # list of description on tool table
            lista_table = lista
           
            field_names = [field.name() for field in fields]
            n = len(field_names)
            lista_attrib = []
            for i in range(0,n):
                f = field_names[i]
                if f==str(Elevation):
                    number = i
                    for feat in layer.getFeatures():
                        attrb = feat.attributes()
                        attribute_read = attrb[number]
                        lista_attrib = lista_attrib + [str(attribute_read)]
            # list of description on attribute table of shapefile
            lista_attributes = lista_attrib           
           
            # without repetitions
            description_common = set(lista_attributes).intersection(lista_table)
            listDescription = list(description_common)
            
            # list of k values
            list_k = []
            for i in range(0, numberRows):
                self.k = self.tableWidget_k.item(i,1)
                list_k = list_k + [str(self.k.text())]                
          
            # list of soil descriptions
            list_soil = []
            for i in range(0, numberRows):
                self.soil = self.tableWidget_k.item(i,0)
                list_soil = list_soil + [str(self.soil.text())]     
           
            # assign the K values to each description in attribute table
            listElements = []
            for j in range(0,len(listDescription)):
                elem = lista_table.index(listDescription[j])
                listElements = listElements + [elem]  
            
            for l in range(0, len(list_k)):
                layer.startEditing()
                exp = QgsExpression(str(list_k[l]))
                #exp.prepare(fields)
                elemDescription = list_soil[l]
                for f in layer.getFeatures():
                    # get attributes of column defined by the user
                    attrb_elem = f[number]
                    if attrb_elem==elemDescription: 
                        f[newFieldIndex] = exp.evaluate()
                        layer.updateFeature(f)  
                layer.commitChanges()   
            list_attrb_newField = []
            for features in layer.getFeatures():
                attrb_newField = features.attributes()
                attrb_newField_read = attrb_newField[number+1]
               
            # update and read the new field
            fieldsNew = layer.fields()
            field_names_new = [newField.name() for newField in fieldsNew]        
            parameter_indexes = field_names_new[newFieldIndex]   
          
            Processing.initialize()
            #Processing.runAlgorithm("grass7:v.to.rast.attribute", None, inputLayer, 0, parameter_indexes, extent, cellSize, -1.0, 0.0001, outPath)
            Processing.runAlgorithm("grass7:v.to.rast", {
                'input': inputLayer,
                'type': [0, 1, 3], 'where': '', 'use': 0, 'attribute_column': parameter_indexes, 'rgb_column': None,
                'label_column': None, 'value': 1, 'memory': 300, 'output': outPath,
                'GRASS_REGION_PARAMETER': None, 'GRASS_REGION_CELLSIZE_PARAMETER': cellSize,
                'GRASS_RASTER_FORMAT_OPT': '',
                'GRASS_RASTER_FORMAT_META': '', 'GRASS_SNAP_TOLERANCE_PARAMETER': -1,
                'GRASS_MIN_AREA_PARAMETER': 0.0001})

            # add result into canvas
            file_info = QFileInfo(outPath)
            if file_info.exists():
                layer_name = file_info.baseName()
            else:
                return False
            rlayer_new = QgsRasterLayer(outPath, layer_name)
            # add result into canvas
            QgsProject.instance().addMapLayer(rlayer_new)
            self.canvas = QgsMapCanvas()
            # self.canvas.scene().addItem(vdlayer)
            QgsProject.instance().addMapLayer(rlayer_new)
            QMessageBox.information(self, self.tr( "Finished" ), self.tr( "RUSLE Factor K completed." ) )