# -*- coding: utf-8 -*-

#******************************************************************************
#
# Freewat
# ---------------------------------------------------------
#
#
# Copyright (C) 2014 - 2015 Iacopo Borsi (iacopo.borsi@tea-group.com)
#
# This source 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.
#
# This code is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# A copy of the GNU General Public License is available on the World Wide Web
# at <http://www.gnu.org/licenses/>. You can also obtain it by writing
# to the Free Software Foundation, 51 Franklin Street, Suite 500 Boston,
# MA 02110-1335 USA.
#
#******************************************************************************

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
import os
from PyQt4 import QtGui, uic
from pyspatialite import dbapi2 as sqlite3
from freewat.freewat_utils import getVectorLayerByName, getVectorLayerNames, getModelsInfoLists, getModelInfoByName, getModelNspecByName, getTransportModelsByName, ComboStyledItemDelegate, deselectAll
from freewat.sqlite_utils import getTableNamesList
from freewat.mdoCreate_utils  import createSftLayer

#
#Load the interface
FORM_CLASS, _ = uic.loadUiType(os.path.join( os.path.dirname(__file__), 'ui/ui_createSFTLayer.ui') )
#
class CreateSFTLayerDialog(QDialog, FORM_CLASS):
    def __init__(self, iface):
        QDialog.__init__(self)
        self.iface = iface
        self.setupUi(self)
        # link the flow model with related transport model(s):
        #If the content of the cmbFlowName combo box containing the name of the flow model changes,
        #then the reloadFields function is recalled
        self.cmbFlowName.currentIndexChanged.connect(self.reloadFields)
        #The label of the 'OK' button is changed to 'Run'
        self.buttonBox.button(QDialogButtonBox.Ok).setText("Run")
        #If the 'Run' button is clicked, the createSFT function below is recalled
        self.buttonBox.button(QDialogButtonBox.Ok).clicked.connect(self.createSFT)
        #The label of the 'OK' button in the buttonBox_2 is changed to 'Run'
        self.buttonBox_2.button(QDialogButtonBox.Ok).setText("Run")
        #If the 'Run' button in the buttonBox_2 is clicked, the createSFT_Table function below is recalled
        self.buttonBox_2.button(QDialogButtonBox.Ok).clicked.connect(self.createSFT_Table)
        #If the 'Cancel' button in the buttonBox_2 is clicked, the Dialog closes
        self.buttonBox_2.button(QDialogButtonBox.Cancel).clicked.connect(self.reject)
        #Recall the manageGui function
        self.manageGui()
##
    def manageGui(self):
        #The cmbFlowName combo box containing the name of the flow model is cleared
        self.cmbFlowName.clear()
        #The list layerNameList is filled with the names of all the vector layers available
        #in the Legend (getVectorLayerNames is a function of the freewat_utils.py module;
        #such function returns a list)
        layerNameList = getVectorLayerNames()
        #The list layerNameList is sorted
        layerNameList.sort()

        # Retrieve modelname and pathfile List
        #The tuple (modelNameList, pathList) is made of two lists:
        # -the list modelNameList is filled with the names of all the flow models available
        # -the list pathList is filled with paths of working directories where the flow models DBs are saved
        #(getModelsInfoLists is a function of the freewat_utils.py module; such function
        #takes the list layerNameList as input, looks for layers whose names start with
        #modeltable and reads the content of the name and working_dir fields in the
        #modeltables found; it returns a tuple)
        (modelNameList, pathList) =  getModelsInfoLists(layerNameList)

        #The cmbFlowName combo box is filled with the elements of the list modelNameList
        self.cmbFlowName.addItems(modelNameList)

        # fill the SFR combobox with only the _sfr layer
        #sfr_layers is a list which will be filled with all the _sfr layers available in the Legend
        sfr_layers = []
        #nametemp iterates over the elements of the list layerNameList
        for nametemp in layerNameList:
            #The elements of the list layerNameList are strings.
            #If the last 4 characters of a string correspond to '_sfr'...
            if nametemp[-4:] == '_sfr':
                #...then the list sfr_layers is updated
                sfr_layers.append(nametemp)

        #The cmbSfrLayer combo box is filled with the elements of the list sfr_layers
        self.cmbSfrLayer.addItems(sfr_layers)

        # fill the SFT combobox with only the _sft layer
        #sft_layers is a list which will be filled with all the _sft layers available in the Legend
        sft_layers = []
        #nametemp iterates over the elements of the list layerNameList
        for nametemp in layerNameList:
            #The elements of the list layerNameList are strings.
            #If the last 4 characters of a string correspond to '_sft'...
            if nametemp[-4:] == '_sft':
                #...then the list sft_layers is updated
                sft_layers.append(nametemp)

        #The cmbSftLayer combo box is filled with the elements of the list sft_layers
        self.cmbSftLayer.addItems(sft_layers)

    def reloadFields(self):
        # Insert list of transport model(s) according with selected flow model:
        #The cmbTransportName combo box containing the name of the transport model is cleared
        self.cmbTransportName.clear()
        #The list layerNameList is filled with the names of all the vector layers available
        #in the Legend (getVectorLayerNames is a function of the freewat_utils.py module;
        #such function returns a list)
        layerNameList = getVectorLayerNames()

        #The list list_of_models is filled with the names of all the transport models available
        #(getTransportModelsByName is a function of the freewat_utils.py module; such function
        #reads the content of the name field in the transport_flow_model table (where flow_model
        #is the name of the flow model read from the cmbFlowName combo box) and returns a list)
        list_of_models = getTransportModelsByName(self.cmbFlowName.currentText())
        #The cmbTransportName combo box is filled with the elements of the list list_of_models
        self.cmbTransportName.addItems(list_of_models)

##
    def createSFT(self):

        self.progressBar.setMinimum(0)
        self.progressBar.setMaximum(0)
        self.progressBar.setValue(0)

        QApplication.processEvents()

        # ------------ Load input data  ------------
        #
        #modelName is the name of the flow model read from the cmbFlowName combo box
        modelName = self.cmbFlowName.currentText()
        #transportName is the name of the transport model read from the cmbTransportName combo box
        transportName = self.cmbTransportName.currentText()

        # Remark: pathfile from model table and number of stress periods (nsp) from time table
        # Retrieve the path of the working directory and the number of stress periods.
        #The tuple (pathfile, nsp ) is made of two objects:
        # -pathfile is the path of the working directiry for a specific flow model
        # -nsp is the number of stress period for a specific flow model
        #(getModelInfoByName is a function of the freewat_utils.py module; such function
        #recalls the getModelsInfoLists function to retrieve the names of all the flow
        #models available in the legend and the corresponding paths, then it retrieves
        #the path of the specific flow model selected in the cmbFlowName combo box and
        #iterates over the rows of the timetable_modelName table to get the number of
        #stress periods; it returns a tuple)
        (pathfile, nsp ) = getModelInfoByName(modelName)
        # Retrieve the number of species, the number of mobile species and the mass units.
        #The tuple (nspec, mob, mass) is made of three objects:
        # -nspec is the number of species simulated for a specific transport model
        # -mob is the number of mobile species simulated for a specific transport model
        # -mass is the mass unit used for a specific transport model
        #(getModelNspecByName is a function of the freewat_utils.py module; such function
        #takes the name of the flow model (modelName, read from the cmbFlowName combo box)
        #and the name of a transport model (transportName, read from the cmbTransportName
        #combo box) as inputs, iterates over the rows of the species_transportName table
        #to get the number of species simulated, reads the content of the is_mobile field
        #and counts only the cells whose content is 'yes', and reads the content of the
        #unit field in the transport_modelName table to get the mass unit; it returns a tuple)
        (nspec, mob, mass) = getModelNspecByName(modelName, transportName)

        # dbName
        dbName = os.path.join(pathfile, modelName + '.sqlite')
        #
        #sfrlayer is the  _sfr layer whose name is read from the cmbSfrLayer combo box
        #(getVectorLayerByName is a function of the freewat_utils.py module; such function
        #retrieves all vector layers available in the Legend)
        sfrlayer = getVectorLayerByName(self.cmbSfrLayer.currentText())
        #newName is the name to be assigned to the _sft MDO (it is read from the txtName line edit)
        newName  = self.txtName.text()

        # Create SFT Layer
        #The createSftLayer function is recalled (createSftLayer is a function of the
        #mdoCreate_utils.py module; such function actually creates the _sfr MDO)
        createSftLayer(sfrlayer, newName, pathfile, modelName, nspec)

########Update values of the reach_ID field in the Attribute Table of the _sft layer just created########
        #sftlayer is the  _sft layer just created, whose name is newName_sft
        sftlayer = getVectorLayerByName(newName+'_sft')

        segid_sfr=[]
        ireach_sfr=[]
        reach_ID_sft=[]

        #f iterates over the rows of the _sft layer
        for f in sftlayer.getFeatures():
            #segid_sfr is the content of the cells in the sftlayer layer whose heading is 'seg_id'
            segid_sfr.append(f['seg_id'])
            #ireach_sfr is the content of the cells in the sftlayer layer whose heading is 'ireach'
            ireach_sfr.append(f['ireach'])
            #reach_ID_sft is the content of the cells in the sftlayer layer whose heading is 'reach_ID'
            reach_ID_sft.append(f['reach_ID'])

        #The zip method allows to get a tuple, whose elements are lists of three
        #elements each ([segid_sfr,ireach_sfr,reach_ID_sft],[segid_sfr,ireach_sfr,reach_ID_sft],...,[segid_sfr,ireach_sfr,reach_ID_sft]).
        #The sorted method allows then to sort the tuple. The sorting is performed
        #based on the first elements of each list (i.e., based on the values of the
        #'seg_id' field). Then, if the first elements of two or more lists are the
        #same, the sorting is performed based on the second elements of each list
        #(i.e., based on the values of the 'ireach' field). Values of the 'ireach'
        #field are unique within each sfr segment
        a=sorted(zip(segid_sfr,ireach_sfr,reach_ID_sft))

        segid_sfr_new=[]
        ireach_sfr_new=[]
        reach_ID_sft_new=[]

        #i iterates over the length of the tuple a
        for i in range(len(a)):
            #The list segid_sfr_new is nothing but the list segid_sfr sorted
            segid_sfr_new.append(a[i][0])
            #The list ireach_sfr_new is nothing but the list ireach_sfr sorted
            ireach_sfr_new.append(a[i][1])
            ##The list reach_ID_sft_new is updated with the third elements
            #of the i-th list of the tuple a (so far reach_ID values are 1)
            reach_ID_sft_new.append(a[i][2])

        #i iterates over the length of the list segid_sfr_new
        for i in range(len(segid_sfr_new)):
            #j iterates over the length of the list segid_sfr
            for j in range(len(segid_sfr)):
                #If the i-th value of segid_sfr_new is equal to the j-th value of segid_sfr
                #and the i-th value of ireach_sfr_new is equal to the j-th value of ireach_sfr...
                if segid_sfr_new[i]==segid_sfr[j] and ireach_sfr_new[i]==ireach_sfr[j]:
                    #...the reach_ID_sft_new value in the j-th position is updated with a
                    #progressive number (remember that i starts from 0!!!)
                    reach_ID_sft_new[j]=i+1

        #Provider for the sftlayer layer
        pr = sftlayer.dataProvider()
        #The editing mode for the sftlayer layer is activated
        sftlayer.startEditing()

        # Add new values to the 'reach_ID' field
        #fields is the list of fields in the Attribute Table of the sftlayer layer
        fields = pr.fields()
        #An index idx is attributed to the 'reach_ID' field
        idx = fields.indexFromName('reach_ID')#retrieve field index from name
        #m is an iterator starting from 0
        m = 0
        #f iterates over the rows of the sftlayer layer
        for f in sftlayer.getFeatures():
            attrs = f.attributes()
            for attr in attrs:
                feat = f.id()
                #The value of that cell (i.e., that row and the 'reach_ID' field) is updated
                #with the value contained in the m-th position of the list reach_ID_sft_new
                sftlayer.changeAttributeValue(feat, idx, reach_ID_sft_new[m])
            #The iterator increases by 1
            m += 1

        #Changes are saved in the Attribute Table of the sftlayer layer
        sftlayer.commitChanges()

        self.progressBar.setMaximum(100)

        #The CreateSFTLayerDialog closes automatically when done
        self.reject()

##
    def createSFT_Table(self):

        self.progressBar_2.setMinimum(0)
        self.progressBar_2.setMaximum(0)
        self.progressBar_2.setValue(0)

        QApplication.processEvents()

        #If the checkbox update_table is checked...
        if self.update_table.isChecked():
            #...then the function updateSFT_Table is recalled...
            self.updateSFT_Table()
            #...and nothing more is done
            return

        # ------------ Load input data  ------------
        #modelName is the name of the flow model read from the cmbFlowName combo box
        modelName = self.cmbFlowName.currentText()
        #transportName is the name of the flow model read from the cmbTransportName combo box
        transportName = self.cmbTransportName.currentText()

        #sftlayer is the  _sft layer whose name is read from the cmbSftLayer combo box
        #(getVectorLayerByName is a function of the freewat_utils.py module; such function
        #retrieves all vector layers available in the Legend)
        sftlayer = getVectorLayerByName(self.cmbSftLayer.currentText())

        # Retrieve the path of the working directory and the number of stress periods.
        #The tuple (pathfile, nsp ) is made of two objects:
        # -pathfile is the path of the working directiry for a specific flow model
        # -nsp is the number of stress period for a specific flow model
        #(getModelInfoByName is a function of the freewat_utils.py module; such function
        #recalls the getModelsInfoLists function to retrieve the names of all the flow
        #models available in the legend and the corresponding paths, then it retrieves
        #the path of the specific flow model selected in the cmbFlowName combo box and
        #iterates over the rows of the timetable_modelName table to get the number of
        #stress periods; it returns a tuple)
        (pathfile, nsp ) = getModelInfoByName(modelName)
        # Retrieve the number of species, the number of mobile species and the mass units.
        #The tuple (nspec, mob, mass) is made of three objects:
        # -nspec is the number of species simulated for a specific transport model
        # -mob is the number of mobile species simulated for a specific transport model
        # -mass is the mass unit used for a specific transport model
        #(getModelNspecByName is a function of the freewat_utils.py module; such function
        #takes the name of the flow model (modelName, read from the cmbFlowName combo box)
        #and the name of a transport model (transportName, read from the cmbTransportName
        #combo box) as inputs, iterates over the rows of the species_transportName table
        #to get the number of species simulated, reads the content of the is_mobile field
        #and counts only the cells whose content is 'yes', and reads the content of the
        #unit field in the transport_modelName table to get the mass unit; it returns a tuple)
        (nspec, mob, mass) = getModelNspecByName(modelName, transportName)

        #Retrieve the DB name and path
        dbname = pathfile + '/' + modelName + ".sqlite"

        #The list tableList is filled with the names of all the tables available
        #in the DB (getTableNamesList is a function of the sqlite_utils.py module;
        #such function returns a list)
        tableList = getTableNamesList(dbname)

        #tableName is the name to be assigned to the sft table
        #(such name is made of the name of the _sft MDO + '_table_' + transportName)
        tableName =  str(self.cmbSftLayer.currentText()) + '_table_' + transportName

        # creating/connecting SQL database object
        con = sqlite3.connect(dbname)
        con.enable_load_extension(True)
        cur = con.cursor()

        #Getting a list of selected features in the sftlayer layer
        features = sftlayer.selectedFeatures()
        selection = list(features)

        isNew = False
        #If the sft table is not in the DB and some selected features in the sftlayer layer exist...
        if (tableName not in tableList and len(selection) > 0):
            isNew = True
            #...then a new table renamed tablename is created.
            #Such table contains the following fields:
            #seg_is (integer), ireach (integer), reach_ID (integer), bc_type (text)
            SQLstring = 'CREATE TABLE "%s" ("seg_id" integer, "ireach" integer, "reach_ID" integer, "bc_type" varchar(30) );'%tableName
            cur.execute(SQLstring)
            #According to the number of species in the transport model and to the stress periods
            #selected in the combo box cmbSP, fields renamed conc_sp_i_spec_n (float) are added
            #(where i is the i-th stress period and n in the n-th species)
            for n in range(nspec):
                for sp in range(nsp):
                    fieldname='conc_sp_' + str(sp+1) + '_spec_' + str(n+1)
                    addColumn = 'ALTER TABLE "%s" ADD COLUMN "%s" float;'%(tableName, fieldname)
                    cur.execute(addColumn)
        #If the sft table is in the DB...
        elif tableName in tableList:
            #...a warning message appears and nothing more is done
            QMessageBox.warning(self, self.tr('Warning!'), self.tr('Table %s already exists!' % tableName))
            return
        #If no selected features in the sftlayer layer exist...
        elif len(selection) == 0:
            #...then a warning message appears and nothing is done
            sftName =  str(self.cmbSftLayer.currentText())
            QMessageBox.warning(self, self.tr('Warning!'), self.tr('Please select at least one reach in the %s layer!' % sftName))
            return

        seg=[]
        reach=[]
        reach_sft=[]
        #feat iterates over the selected features
        for feat in features:
            #The lists seg, reach and reach_sft are updated with
            #values of the seg_id, ireach and reach_ID fields
            seg.append(int(feat['seg_id']))
            reach.append(int(feat['ireach']))
            reach_sft.append(int(feat['reach_ID']))

        #column_names is the list of columns headings. It contains at the beginning
        #the heading of the first four columns only ('seg_id','ireach','reach_ID','bc_type')
        column_names=['seg_id','ireach','reach_ID','bc_type']
        #n iterates over the number of species
        for n in range(nspec):
            #sp iterates over the number of stress periods
            for sp in range(nsp):
                #The list column_names is updated with the headings of the remaining fields
                column_names.append('conc_sp_' + str(sp+1) + '_spec_' + str(n+1))

        #cell_values is the list of values at each cell of each row of the sft table
        cell_values=[]

        #row is a new dictionary. Its content will be like:
        #row={'seg_id':1,'ireach':1,'reach_ID':1,'bc_type':'Input bc type...','conc_sp_1_spec_1':0.0,'conc_sp_1_spec_1':0.0,...,'conc_sp_n_spec_m':0.0}
        #The keys of this dictionary will be the elements of the list column_names,
        #the values of this dictionary will be the elements of the list cell_values
        row={}

        #i iterates over the length of the list seg (which also corresponds
        #to the number of rows which will be added in the sft table)
        for i in range(len(seg)):
            #The list cell_values is updated with the seg_id value
            cell_values.append(seg[i])
            #The list cell_values is updated with the ireach value
            cell_values.append(reach[i])
            #The list cell_values is updated with the reach_ID value
            cell_values.append(reach_sft[i])
            #The list cell_values is updated with the string 'Input bc type...'
            cell_values.append('Input bc type...')
            #n iterates over the number of species
            for n in range(nspec):
                #sp iterates over the number of stress periods
                for sp in range(nsp):
                    #The list cell_values is updated with the value 0.0
                    #(0.0 is the concentration associated to a specific bc)
                    cell_values.append(0.0)
            #The built-in function enumerate allows to iterate over the list column_names.
            #The outputs of this function are:
            # - index is the index ID of each element of the list column_names
            # - key is the value of each element of the list column_names
            for index, key in enumerate(column_names):
                #The dictionary row is filled
                row[key]=cell_values[index]
            #A new row is added into the sft table (its content is
            #nothing but the content of the dictionary row)
            columns = ', '.join(map(str, row.keys()))
            values = ', '.join(map(repr, row.values()))
            cur.execute('INSERT INTO "{0}" ({1}) VALUES ({2})'.format(tableName, columns, values))
            #The list cell_values and the dictionary row are emptied
            cell_values=[]
            row={}

        # Close SpatiaLiteDB
        cur.close()
        # Save the changes
        con.commit()
        # Close connection
        con.close()

        #Add the sft table into QGis map, if this is the first time!!
        if isNew:
            uri = QgsDataSourceURI()
            uri.setDatabase(dbname)
            schema = ''
            table = tableName
            geom_column = None
            uri.setDataSource(schema, table, geom_column)
            display_name = tableName
            tableLayer = QgsVectorLayer(uri.uri(), display_name, 'spatialite')

            QgsMapLayerRegistry.instance().addMapLayer(tableLayer)

        self.progressBar_2.setMaximum(100)

        #Close the dialog window after the execution of the algorithm
        self.reject()

        #Deselect all features (deselectAll is a function of the freewat_utils.py module)
        deselectAll()

    def updateSFT_Table(self):

        self.progressBar_2.setMinimum(0)
        self.progressBar_2.setMaximum(0)
        self.progressBar_2.setValue(0)

        QApplication.processEvents()

        # ------------ Load input data  ------------
        #modelName is the name of the flow model read from the cmbFlowName combo box
        modelName = self.cmbFlowName.currentText()
        #transportName is the name of the flow model read from the cmbTransportName combo box
        transportName = self.cmbTransportName.currentText()

        #sftlayer is the  _sft layer whose name is read from the cmbSftLayer combo box
        #(getVectorLayerByName is a function of the freewat_utils.py module; such function
        #retrieves all vector layers available in the Legend)
        sftlayer = getVectorLayerByName(self.cmbSftLayer.currentText())

        # Retrieve the path of the working directory and the number of stress periods.
        #The tuple (pathfile, nsp ) is made of two objects:
        # -pathfile is the path of the working directiry for a specific flow model
        # -nsp is the number of stress period for a specific flow model
        #(getModelInfoByName is a function of the freewat_utils.py module; such function
        #recalls the getModelsInfoLists function to retrieve the names of all the flow
        #models available in the legend and the corresponding paths, then it retrieves
        #the path of the specific flow model selected in the cmbFlowName combo box and
        #iterates over the rows of the timetable_modelName table to get the number of
        #stress periods; it returns a tuple)
        (pathfile, nsp ) = getModelInfoByName(modelName)
        # Retrieve the number of species, the number of mobile species and the mass units.
        #The tuple (nspec, mob, mass) is made of three objects:
        # -nspec is the number of species simulated for a specific transport model
        # -mob is the number of mobile species simulated for a specific transport model
        # -mass is the mass unit used for a specific transport model
        #(getModelNspecByName is a function of the freewat_utils.py module; such function
        #takes the name of the flow model (modelName, read from the cmbFlowName combo box)
        #and the name of a transport model (transportName, read from the cmbTransportName
        #combo box) as inputs, iterates over the rows of the species_transportName table
        #to get the number of species simulated, reads the content of the is_mobile field
        #and counts only the cells whose content is 'yes', and reads the content of the
        #unit field in the transport_modelName table to get the mass unit; it returns a tuple)
        (nspec, mob, mass) = getModelNspecByName(modelName, transportName)

        #Retrieve the DB name and path
        dbname = pathfile + '/' + modelName + ".sqlite"

        #The list tableList is filled with the names of all the tables available
        #in the DB (getTableNamesList is a function of the sqlite_utils.py module;
        #such function returns a list)
        tableList = getTableNamesList(dbname)

        #tableName is the name to be assigned to the sft table
        #(such name is made of the name of the _sft MDO + '_table_' + transportName)
        tableName =  str(self.cmbSftLayer.currentText()) + '_table_' + transportName

        # creating/connecting SQL database object
        con = sqlite3.connect(dbname)
        con.enable_load_extension(True)
        cur = con.cursor()

        #Getting a list of selected features in the sftlayer layer
        features = sftlayer.selectedFeatures()
        selection = list(features)

        #If the sft table is already in the DB and some selected features in the sftlayer layer exist...
        if (tableName in tableList and len(selection) > 0):
            seg=[]
            reach=[]
            reach_sft=[]
            #feat iterates over the selected features
            for feat in features:
                #The lists seg, reach and reach_sft are updated with
                #values of the seg_id, ireach and reach_ID fields
                seg.append(int(feat['seg_id']))
                reach.append(int(feat['ireach']))
                reach_sft.append(int(feat['reach_ID']))

            #column_names is the list of columns headings. It contains at the beginning
            #the heading of the first four columns only ('seg_id','ireach','reach_ID','bc_type')
            column_names=['seg_id','ireach','reach_ID','bc_type']
            #n iterates over the number of species
            for n in range(nspec):
                #sp iterates over the number of stress periods
                for sp in range(nsp):
                    #The list column_names is updated with the headings of the remaining fields
                    column_names.append('conc_sp_' + str(sp+1) + '_spec_' + str(n+1))

            #cell_values is the list of values at each cell of each row of the sft table
            cell_values=[]

            #row is a new dictionary. Its content will be like:
            #row={'seg_id':1,'ireach':1,'reach_ID':1,'bc_type':'Input bc type...','conc_sp_1_spec_1':0.0,'conc_sp_1_spec_1':0.0,...,'conc_sp_n_spec_m':0.0}
            #The keys of this dictionary will be the elements of the list column_names,
            #the values of this dictionary will be the elements of the list cell_values
            row={}

            #i iterates over the length of the list seg (which also corresponds
            #to the number of rows which will be added in the sft table)
            for i in range(len(seg)):
                #The list cell_values is updated with the seg_id value
                cell_values.append(seg[i])
                #The list cell_values is updated with the ireach value
                cell_values.append(reach[i])
                #The list cell_values is updated with the reach_ID value
                cell_values.append(reach_sft[i])
                #The list cell_values is updated with the string 'Input bc type...'
                cell_values.append('Input bc type...')
                #n iterates over the number of species
                for n in range(nspec):
                    #sp iterates over the number of stress periods
                    for sp in range(nsp):
                        #The list cell_values is updated with the value 0.0
                        #(0.0 is the concentration associated to a specific bc)
                        cell_values.append(0.0)
                #The built-in function enumerate allows to iterate over the list column_names.
                #The outputs of this function are:
                # - index is the index ID of each element of the list column_names
                # - key is the value of each element of the list column_names
                for index, key in enumerate(column_names):
                    #The dictionary row is filled
                    row[key]=cell_values[index]
                #A new row is added into the sft table (its content is
                #nothing but the content of the dictionary row)
                columns = ', '.join(map(str, row.keys()))
                values = ', '.join(map(repr, row.values()))
                cur.execute('INSERT INTO "{0}" ({1}) VALUES ({2})'.format(tableName, columns, values))
                #The list cell_values and the dictionary row are emptied
                cell_values=[]
                row={}

        #If the sft table is not in the DB...
        elif tableName not in tableList:
            #...a warning message appears and nothing more is done
            QMessageBox.warning(self, self.tr('Warning!'), self.tr('You must create table %s first and then check "Update existing SFT Table"!' % tableName))
            return
        #If no selected features in the sftlayer layer exist...
        elif len(selection) == 0:
            #...then a warning message appears and nothing is done
            sftName =  str(self.cmbSftLayer.currentText())
            QMessageBox.warning(self, self.tr('Warning!'), self.tr('Please select at least one reach in the %s layer!' % sftName))
            return

        # Close SpatiaLiteDB
        cur.close()
        # Save the changes
        con.commit()
        # Close connection
        con.close()

        self.progressBar_2.setMaximum(100)

        #Close the dialog window after the execution of the algorithm
        self.reject()

        #Deselect all features (deselectAll is a function of the freewat_utils.py module)
        deselectAll()
