# -*- coding: utf-8 -*-
"""
/***************************************************************************
 CreateGraphDialog
                                 A QGIS plugin
 This is a new feature of the Graphab plugin that allow you to rasterize
 vector files and merge them into one raster file. You can also merge
 direct raster files.
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                             -------------------
        begin                : 2021-04-07
        git sha              : $Format:%H$
        copyright            : (C) 2021 by ThéMA
        email                : robin.marlinl@gmail.com
        author               : Robin Marlin-Lefebvre
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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
import json
import tempfile

import processing
import copy

from qgis.PyQt import uic
from qgis.PyQt import QtWidgets

from PyQt5.QtWidgets import QTableWidgetItem, QFileDialog, QMessageBox, QHeaderView
from PyQt5.QtCore import QFileInfo, Qt, QVariant, QCoreApplication
from PyQt5.QtGui import QTextCursor, QFontMetrics

from qgis.core import (
    QgsVectorLayer,
    QgsRasterLayer,
    QgsRectangle,
    QgsProcessing,
    QgsProject,
    QgsCoordinateReferenceSystem,
    QgsField
)

# Import of windows that allow you to add a vector or raster file with options
from .raster_dialog import RasterDialog
from .vector_dialog import VectorDialog

# Import class to show log in our window
from .MyFeedBack import MyFeedBack

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


class OsRaster(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, plugin):
        """Constructor."""
        super(OsRaster, self).__init__(None)
        # Set up the user interface from Designer through FORM_CLASS.
        # After self.setupUi() you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)

        # initialize specifications about how window elements are in the beginning
        header = self.rasterTableWidget.horizontalHeader()
        header.setSectionResizeMode(QHeaderView.ResizeToContents)
        header.setSectionResizeMode(0, QHeaderView.Stretch)

        header = self.vectorTableWidget.horizontalHeader()
        header.setSectionResizeMode(QHeaderView.ResizeToContents)
        header.setSectionResizeMode(0, QHeaderView.Stretch)

        header = self.orderTableWidget.horizontalHeader()
        header.setSectionResizeMode(QHeaderView.ResizeToContents)
        header.setSectionResizeMode(0, QHeaderView.Stretch)

        # initialize connections between actions on window elements and functions
        self.mTabWidget.currentChanged.connect(self.findAction)

        self.addRasterToolButton.clicked.connect(self.addRaster)
        self.editRasterToolButton.clicked.connect(self.editRaster)
        self.removeRasterToolButton.clicked.connect(self.removeRaster)
        self.rasterUpToolButton.clicked.connect(lambda: self.moveUp('raster'))
        self.rasterDownToolButton.clicked.connect(lambda: self.moveDown('raster'))
        self.rasterTableWidget.itemSelectionChanged.connect(self.updateRasterButtonFromSelection)
        self.rasterTableWidget.doubleClicked.connect(self.editRaster)
        self.rasterTableWidget.horizontalHeader().sectionResized.connect(
            lambda idx, old, new:
                self.resizeAllFilePath(idx, new, 'raster')
        )

        self.addVectorToolButton.clicked.connect(self.addVector)
        self.editVectorToolButton.clicked.connect(self.editVector)
        self.removeVectorToolButton.clicked.connect(self.removeVector)
        self.vectorUpToolButton.clicked.connect(lambda: self.moveUp('vector'))
        self.vectorDownToolButton.clicked.connect(lambda: self.moveDown('vector'))
        self.vectorTableWidget.itemSelectionChanged.connect(self.updateVectorButtonFromSelection)
        self.vectorTableWidget.doubleClicked.connect(self.editVector)
        self.vectorTableWidget.horizontalHeader().sectionResized.connect(
            lambda idx, old, new:
                self.resizeAllFilePath(idx, new, 'vector')
        )

        #self.extentComboBox.currentIndexChanged.connect(self.calculateExtentFromLayer)

        self.orderUpToolButton.clicked.connect(lambda: self.moveMultipleOrder('up'))
        self.orderDownToolButton.clicked.connect(lambda: self.moveMultipleOrder('down'))
        self.orderFirstToolButton.clicked.connect(lambda: self.moveMultipleOrder('first'))
        self.orderLastToolButton.clicked.connect(lambda: self.moveMultipleOrder('last'))

        self.orderTableWidget.__class__.dropEvent = self.rowDropped
        self.orderTableWidget.cellChanged.connect(self.updateOrderTab)
        self.orderTableWidget.horizontalHeader().sectionResized.connect(
            lambda idx, old, new:
                self.resizeAllFilePath(idx, new, 'order')
        )

        self.exportToolButton.clicked.connect(self.fileSaveJSON)
        self.importToolButton.clicked.connect(self.fileImportJSON)

        self.startPushButton.clicked.connect(self.mergeRasters)
        self.closePushButton.clicked.connect(self.reject)

        self.resolutionLineEdit.textChanged.connect(self.updateNumberPixelFromExtentAndResolution)
        self.extentSelection.extentChanged.connect(self.updateNumberPixelFromExtentAndResolution)

        self.logTextEdit.textChanged.connect(self.moveCursor)

        self.clearToolButton.clicked.connect(self.clearAll)

        # initialize globals variables
        self.plugin = plugin

        self.RasterDialog = None
        self.VectorDialog = None

        self.rasterTab = []
        self.vectorTab = []

        self.orderTabPopulated = []

        self.extentTab = []

        self.needOrderUpdate = False
        self.needOutputUpdate = False

        self.orderTabIsUpdated = False

        # Use for log message from gdal processing
        self.feedback = MyFeedBack()
        self.feedback.setOsRaster(self)
        self.feedback.progressChanged.connect(self.progress_changed)

        # Use for ignore raster with to much code values
        self.maxUniquesCodePossible = 1000

        # Default nodata value
        self.nodataDefault = 255

        # Use for translation
        self.bool = [self.translate('osraster', 'True'), self.translate('osraster', 'False')]

        # EXTREMELY IMPORTANT VARIABLE IF windowProcess NOT STOCK HE CAN'T USE POSTPROCESSALGORITHM()
        self.windowProcess = None

    # --------------------------------------------------------------------------

    def showDialog(self):
        """Initialize basic window and waiting for any action of the user"""

        # able to not block the user on the main window
        self.show()

        # keep the main window open
        self.exec_()

    # --------------------------------------------------------------------------

    def updateRasterButtonFromSelection(self):
        # get the row selected in the raster table widget
        row = self.rasterTableWidget.currentRow()

        # if no row is selected disable the edit and remove button else enable them
        if row < 0:
            self.editRasterToolButton.setEnabled(False)
            self.removeRasterToolButton.setEnabled(False)
        else:
            self.editRasterToolButton.setEnabled(True)
            self.removeRasterToolButton.setEnabled(True)

    def updateVectorButtonFromSelection(self):
        # get the row selected in the vector table widget
        row = self.vectorTableWidget.currentRow()

        # if no row is selected disable the edit and remove button else enable them
        if row < 0:
            self.editVectorToolButton.setEnabled(False)
            self.removeVectorToolButton.setEnabled(False)
        else:
            self.editVectorToolButton.setEnabled(True)
            self.removeVectorToolButton.setEnabled(True)

    # --------------------------------------------------------------------------

    def addRaster(self):
        # add log lines translated
        self.logTextEdit.insertPlainText(self.translate('osraster', '\n->NEW RASTER\n'))

        # creation of an OsRaster_option_raster_window and initialization
        self.RasterDialog = RasterDialog(self.feedback, self.maxUniquesCodePossible)

        # waiting for a result like None or multiple data
        returnRaster = self.RasterDialog.showDialog()

        # if not None : get the rasterTableWidget and add a new row with different wanted elements
        if returnRaster is not None:
            if not self.alreadyAddedFile(returnRaster["file"], self.rasterTab):
                self.rasterTab.append(returnRaster)
                indexRaster = self.rasterTableWidget.rowCount()
                self.rasterTableWidget.setRowCount(indexRaster + 1)

                self.setRowRaster(returnRaster, indexRaster)
                self.updateNeeded()

    def addVector(self):
        # add log lines translated
        self.logTextEdit.insertPlainText(self.translate('osraster', '\n->NEW VECTOR\n'))

        # creation of an OsRaster_option_vector_window and initialization
        self.VectorDialog = VectorDialog(self.feedback, self.maxUniquesCodePossible)

        # waiting for a result like None or multiple data
        returnVector = self.VectorDialog.showDialog()

        # get the vectorTableWidget and add a new row with different wanted elements
        if returnVector is not None:
            if not self.alreadyAddedFile(returnVector["file"], self.vectorTab):
                self.vectorTab.append(returnVector)
                indexVector = self.vectorTableWidget.rowCount()
                self.vectorTableWidget.setRowCount(indexVector + 1)

                self.setRowVector(returnVector, indexVector)
                self.updateNeeded()

    def alreadyAddedFile(self, file, tab):
        # for each loaded file in the tab return true if no file path are the same
        for element in tab:
            if file == element["file"]:
                self.openInfoWindow(
                    self.translate('osraster', "ERROR - File"),
                    self.translate('osraster', "This file is already loaded :\n%s") % file
                )
                return True
        return False

    # --------------------------------------------------------------------------

    def editRaster(self):
        # get all selected lines from rasterTableWidget
        row = self.rasterTableWidget.currentRow()

        # if at least one line selected : open a window with the file info to edit the row
        if row >= 0:
            # creation of an OsRaster_option_raster_window and initialization
            self.RasterDialog = RasterDialog(self.feedback, self.maxUniquesCodePossible)

            # waiting for a result like None or multiple data
            returnRaster = self.RasterDialog.showDialog(self.rasterTab[row])

            if returnRaster is not None:
                self.rasterTab[row] = returnRaster
                self.setRowRaster(returnRaster, row)
                self.updateNeeded()

    def editVector(self):
        # get all selected lines from vectorTableWidget
        row = self.vectorTableWidget.currentRow()

        # if at least one line selected : open a window with the file info to edit the row
        if row >= 0:
            # creation of an OsRaster_option_vector_window and initialization
            self.VectorDialog = VectorDialog(self.feedback, self.maxUniquesCodePossible)

            # waiting for a result like None or multiple data
            returnVector = self.VectorDialog.showDialog(self.vectorTab[row])

            if returnVector is not None:
                self.vectorTab[row] = returnVector
                self.setRowVector(returnVector, row)
                self.updateNeeded()

    # --------------------------------------------------------------------------

    def setRowRaster(self, returnRaster, indexRaster, multipleRows=False):
        # create table widget item for each wanted value
        itemPath = QTableWidgetItem(returnRaster["file"])
        itemNewEncoding = QTableWidgetItem(self.translate('osraster', str(not returnRaster["sameEncoding"])))

        # add new items to the rasterTableWidget
        self.rasterTableWidget.setItem(indexRaster, 0, itemPath)
        self.rasterTableWidget.setItem(indexRaster, 1, itemNewEncoding)

        # resize columns depending of the size of them contents
        if not multipleRows:
            self.resizeAllFilePath(0, self.rasterTableWidget.columnWidth(0), 'raster')

    def setRowVector(self, returnVector, indexVector, multipleRows=False):
        # create table widget item for each wanted value
        bufferValueVector = returnVector["bufferValue"]
        if bufferValueVector is None:
            bufferValueVector = False

        itemPath = QTableWidgetItem(returnVector["file"])
        itemValueBurnIn = QTableWidgetItem(str(returnVector["burnInValue"]))
        itemAllTouch = QTableWidgetItem(self.translate('osraster', str(returnVector["allTouch"])))
        itemBufferValue = QTableWidgetItem(self.translate('osraster', str(bufferValueVector)))

        # add new items to the vectorTableWidget
        self.vectorTableWidget.setItem(indexVector, 0, itemPath)
        self.vectorTableWidget.setItem(indexVector, 1, itemValueBurnIn)
        self.vectorTableWidget.setItem(indexVector, 2, itemAllTouch)
        self.vectorTableWidget.setItem(indexVector, 3, itemBufferValue)

        # resize columns depending of the size of them contents
        if not multipleRows:
            self.resizeAllFilePath(0, self.vectorTableWidget.columnWidth(0), 'vector')

    def setRowOrder(self, indexOrder, file, oldCode, newCode):
        # create table widget item for each wanted value
        itemFile = QTableWidgetItem(str(file))
        itemFile.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
        itemOriginalCode = QTableWidgetItem(str(oldCode))
        itemOriginalCode.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)

        # add new items to the orderTableWidget
        self.orderTableWidget.setItem(indexOrder, 0, itemFile)
        self.orderTableWidget.setItem(indexOrder, 1, itemOriginalCode)
        self.orderTableWidget.setItem(indexOrder, 2, QTableWidgetItem(str(newCode)))

    # --------------------------------------------------------------------------

    def resizeAllFilePath(self, logicalIndex, newSize, type):
        # if the column moved is not the filepath column
        if logicalIndex != 0:
            return

        # depending of the tableWidget that need to update all filepath we clone the right list and right tableWidget
        if type == 'raster':
            tableWidget = self.rasterTableWidget
            tab = self.rasterTab
        elif type == 'vector':
            tableWidget = self.vectorTableWidget
            tab = self.vectorTab
        elif type == 'order':
            tableWidget = self.orderTableWidget
            tab = self.orderTabPopulated
        else:
            return

        self.orderTabIsUpdated = False

        # for each filepath we recreate a an elided string to fit perfectly with the size of the filepath column
        font = tableWidget.font()
        font.setPointSize(font.pointSize() + 1)
        fontMetric = QFontMetrics(font)
        for row in range(len(tab)):
            filePath = fontMetric.elidedText(tab[row]["file"], Qt.ElideLeft, newSize)
            tableWidget.item(row, 0).setText(filePath)
        self.orderTabIsUpdated = True

    # --------------------------------------------------------------------------

    def removeRaster(self):
        # get all selected lines from rasterTableWidget
        row = self.rasterTableWidget.currentRow()

        # if at least one line selected : remove it from the qTable and the rasterTab
        if row >= 0:
            self.rasterTableWidget.removeRow(row)
            self.rasterTab.pop(row)
            self.updateNeeded()

    def removeVector(self):
        # get all selected lines from vectorTableWidget
        row = self.vectorTableWidget.currentRow()

        # if at least one line selected : remove it from the qTable and the vectorTab
        if row >= 0:
            self.vectorTableWidget.removeRow(row)
            self.vectorTab.pop(row)
            self.updateNeeded()

    # --------------------------------------------------------------------------

    def moveUp(self, type, row=None, moveDistance=1):
        # depending of the tableWidget that need to move a row we clone the right list and right tableWidget
        if type == 'raster':
            tableWidget = self.rasterTableWidget
            tab = self.rasterTab
            self.updateNeeded()
        elif type == 'vector':
            tableWidget = self.vectorTableWidget
            tab = self.vectorTab
            self.updateNeeded()
        elif type == 'order':
            tableWidget = self.orderTableWidget
            tab = self.orderTabPopulated
        else:
            return

        # if there is no index of row we find it on the cloned tableWidget
        if row is None:
            row = tableWidget.currentRow()

        # if the row index is superior than 0
        if row > 0:
            # add a new row to the new position
            tableWidget.insertRow(row - moveDistance)
            # for each element in the old row we move them to the new row
            for i in range(tableWidget.columnCount()):
                tableWidget.setItem(row - moveDistance, i, tableWidget.takeItem(row + 1, i))
            # change the selection for the new row
            tableWidget.setCurrentCell(row - moveDistance, 0)
            # remove the old row
            tableWidget.removeRow(row + 1)

            # update tab index
            tab.insert(row - moveDistance, tab.pop(row))

    def moveDown(self, type, row=None, moveDistance=2):
        # depending of the tableWidget that need to move a row we clone the right list and right tableWidget
        if type == 'raster':
            tableWidget = self.rasterTableWidget
            tab = self.rasterTab
            self.updateNeeded()
        elif type == 'vector':
            tableWidget = self.vectorTableWidget
            tab = self.vectorTab
            self.updateNeeded()
        elif type == 'order':
            tableWidget = self.orderTableWidget
            tab = self.orderTabPopulated
        else:
            return

        # if there is no index of row we find it on the cloned tableWidget
        if row is None:
            row = tableWidget.currentRow()

        # if the row index is between (0) included and the (maximum size-1) excluded
        if tableWidget.rowCount() - 1 > row >= 0:
            # add a new row to the new position
            tableWidget.insertRow(row + moveDistance)
            # for each element in the old row we move them to the new row
            for i in range(tableWidget.columnCount()):
                tableWidget.setItem(row + moveDistance, i, tableWidget.takeItem(row, i))
            # change the selection for the new row
            tableWidget.setCurrentCell(row + moveDistance, 0)
            # remove the old row
            tableWidget.removeRow(row)

            # update tab index
            tab.insert(row + (moveDistance - 1), tab.pop(row))

    # --------------------------------------------------------------------------

    def findAction(self):
        # get the index of the selected tab
        indexTab = self.mTabWidget.currentIndex()

        # if the output tab is selected and has to be updated we update it
        if indexTab == 2 and self.needOutputUpdate:
            self.updateOutputTab()

        # else if the merge tab is selected and has to be updated we update it
        elif indexTab == 1 and self.needOrderUpdate:
            self.populateOrderTable()

    # --------------------------------------------------------------------------

    def updateOutputTab(self):
        self.needOutputUpdate = False


    # --------------------------------------------------------------------------

    def updateNumberPixelFromExtentAndResolution(self):
        # get the extents values set and the resolution
        rect = self.extentSelection.outputExtent()
        resolution = self.resolutionLineEdit.text()

        # if one of them is not a float we set the numberLabel to not a number value
        if not self.isFloat(resolution):
            self.numberLabel.setText("NaN")
            return

        resolution = float(resolution)
        # prevent division by 0
        if rect.isEmpty() or resolution == 0:
            self.numberLabel.setText(str(0))
            return

        # calculate the size of the extent in pixel
        self.numberLabel.setText(str((rect.width() / resolution) * (rect.height() / resolution)))

        self.outputQgsProjectionSelectionWidget.setCrs(self.extentSelection.outputCrs())

    # --------------------------------------------------------------------------

    def moveMultipleOrder(self, type):
        # prevent the call of a function connect to the edit orderTableWidget cell event
        self.orderTabIsUpdated = False

        # get all uniques rows selected in the orderTableWidget sorted
        allSelectedRow = self.orderTableWidget.selectionModel().selectedRows()
        allSelectedRow = [uniqueRow.row() for uniqueRow in allSelectedRow]
        allSelectedRow.sort()

        index = 0
        if type == 'down' or type == 'last':
            index = self.orderTableWidget.rowCount() - 1
            allSelectedRow.reverse()

        self.orderTableWidget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
        for selectedRow in allSelectedRow:
            if type == 'down':
                if selectedRow == index:
                    index -= 1
                    continue
                self.moveDown('order', selectedRow)
            elif type == 'up':
                if selectedRow == index:
                    index += 1
                    continue
                self.moveUp('order', selectedRow)
            elif type == 'last':
                self.moveDown('order', selectedRow, (index + 1) - selectedRow)
                index -= 1
            elif type == 'first':
                self.moveUp('order', selectedRow, selectedRow - index)
                index += 1

        self.orderTableWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
        self.orderTabIsUpdated = True

    def populateOrderTable(self):
        self.needOrderUpdate = False

        # prevent the call of a function connect to the edit orderTableWidget cell event
        self.orderTabIsUpdated = False

        # remove all row of the orderTableWidget
        self.orderTableWidget.setRowCount(0)
        self.orderTabPopulated = []

        if self.mTabWidget.currentIndex() == 1:
            # Merge vectorTab with rasterTab by using a module that copy them to prevent editing original object
            #orderTab = copy.deepcopy(self.vectorTab) + copy.deepcopy(self.rasterTab)
            orderTab = self.vectorTab + self.rasterTab

            # Add a number of row depending of the size of orderTab * each different codes
            sizeOrder = 0
            for element in orderTab:
                sizeOrder += element["size"]
            self.orderTableWidget.setRowCount(sizeOrder)

            # Fill lines
            counter = 0
            for element in orderTab:
                file = element["file"]

                encoding = element["encoding"]

                for (key, value) in encoding.items():
                    self.setRowOrder(counter, file, key, value)
                    self.orderTabPopulated.append({
                        "file": file,
                        "oldCode": key,
                        "newCode": value,
                        "size": element["size"]
                    })
                    counter += 1

        # adapt the size of filepath depending of the column size
        self.resizeAllFilePath(0, self.orderTableWidget.columnWidth(0), 'order')

        # allow the call of a function connect to the edit orderTableWidget cell event
        self.orderTabIsUpdated = True

    # function use to verify the edition of an encoding cell if the new code use works and edit the tab
    def updateEncoding(self, file, oldCode, newCode):
        # if the new code is not a nodata value
        if newCode != '':
            # if the new code is not between 0 and 65 535 an error is raise because the raster is on 16bits positives
            if newCode < 0:
                self.openInfoWindow(
                    self.translate('osraster', "ERROR - Incorrect code value"),
                    self.translate('osraster', "A code value can't be negative.")
                )
                return -1
            elif newCode > 65535:
                self.openInfoWindow(
                    self.translate('osraster', "ERROR - Incorrect code value"),
                    self.translate('osraster', "A code value can't be greater than 65 535.")
                )
                return -1

        sameEncoding = True
        allFileTab = self.vectorTab + self.rasterTab

        # search the file encoding edited
        for element in allFileTab:
            if element["file"] == file:
                # if it's a vector with fixed value the user can set the value to nodata
                if "valueType" in element and element["valueType"] == 'Fixed':
                    if newCode == '':
                        self.openInfoWindow(
                            self.translate('osraster', "ERROR - Nodata value"),
                            self.translate('osraster', "Fixed vector value can't be a nodata value.")
                        )
                        return -1
                    # if not a nodata value update the fixed value with the new code
                    element["burnInValue"] = newCode

                # for each code in the file encoding find the edited code to replace it
                for (key, value) in element["encoding"].items():
                    if key == oldCode:
                        value = newCode
                        element["encoding"][key] = value

                    # verify if the encoding is the original one
                    if key != value:
                        sameEncoding = False

                element["sameEncoding"] = sameEncoding
                return 0
        return -1

    # function use to edit internal encoding tab from the orderTableWidget
    def updateOrderTab(self, row, column):
        if self.orderTabIsUpdated:
            # prevent an infinite loop
            self.orderTabIsUpdated = False

            # get the code set on the table widget and the actual encoding for the row
            itemNewCode = self.orderTableWidget.item(row, column)
            newCode = itemNewCode.text()
            oldCode = self.orderTabPopulated[row]["oldCode"]
            tempCode = self.orderTabPopulated[row]["newCode"]

            # if the new code is a nodata value or an integer
            if newCode == '' or self.isInteger(newCode):
                if self.isInteger(newCode):
                    newCode = int(newCode)

                # verify the new code
                req = self.updateEncoding(self.orderTabPopulated[row]["file"], oldCode, newCode)

                # if the code is not valid: come back to the previous one else: edit the row of orderTabPopulated
                if req < 0:
                    itemNewCode.setText(str(tempCode))
                else:
                    self.orderTabPopulated[row]["newCode"] = newCode

            # if the code is not valid: come back
            else:
                self.openInfoWindow(
                    self.translate('osraster', "ERROR - Incorrect code value"),
                    self.translate('osraster', "Encoding code have to be an integer.")
                )
                itemNewCode.setText(str(tempCode))

            # reactive the event
            self.orderTabIsUpdated = True

    def rasterizeAllVector(self, crs):
        exVectorTab = {}

        # get nodata value and if not set use the default one
        nodata = self.nodataLineEdit.text()
        if self.isValidNoDataValue(nodata) and 0 <= int(float(nodata)) <= 65535:
            nodata = int(float(nodata))
        else:
            nodata = self.nodataDefault

        extent = self.extentSelection.outputExtent()

        # for each vector if needed we reprojecte it, add a buffer, add a new field then rasterize it
        for vector in self.vectorTab:

            self.logTextEdit.insertPlainText(self.translate('osraster', '\n\n->VECTOR : %s\n') % vector["name"])

            # extract zone
            file = processing.run("native:extractbyexpression", {
                'INPUT': vector["layer"],
                'EXPRESSION': "intersects($geometry, make_rectangle_3points(make_point("
                              + str(extent.xMinimum()) + "," + str(extent.yMinimum()) +
                              "), make_point(" + str(extent.xMinimum()) + "," + str(extent.yMaximum()) +
                              "), make_point(" + str(extent.xMaximum()) + "," + str(extent.yMaximum()) + ")))",
                'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT}, feedback=self.feedback)['OUTPUT']

            # change the crs of the vector as wanted
            if file.crs().authid() != crs:
                self.logTextEdit.insertPlainText(self.translate('osraster', '\n->REPROJECTION VECTOR\n'))
                file = self.reprojectVectorWithCRS(file, crs)

            # adding buffer if the user want buffer
            if vector["bufferValue"] is not None:
                self.logTextEdit.insertPlainText(self.translate('osraster', '\n->BUFFERIZATION\n'))
                file = processing.run("native:buffer", {'INPUT': file,
                                                        'DISTANCE': vector["bufferValue"],
                                                        'SEGMENTS': 5,
                                                        'END_CAP_STYLE': 0,
                                                        'JOIN_STYLE': 0,
                                                        'MITER_LIMIT': 2,
                                                        'DISSOLVE': False,
                                                        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT},
                                      feedback=self.feedback)['OUTPUT']
                self.progressBar.setValue(100)

            # if the vector use a string field as burn-in value we clone it and add a new integer field
            if vector["stringField"]:
                self.logTextEdit.insertPlainText('\n->CLONING\n')

                # clone the original vector
                file.selectAll()
                clone_layer = processing.run("native:saveselectedfeatures", {'INPUT': file,
                                                                             'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT},
                                             feedback=self.feedback)['OUTPUT']
                file.removeSelection()

                self.logTextEdit.insertPlainText(
                    self.translate('osraster', '\n->ADDING NEW ENCODING FROM STRING FIELD\n')
                )

                # add a integer field
                clone_layer.startEditing()
                clone_layer.addAttribute(QgsField("FieldUseForTempCodeOsRaster", QVariant.Int, "integer"))

                encoding = vector["encoding"]
                counter = 0
                total = clone_layer.featureCount()
                fields = clone_layer.fields()
                index = fields.indexOf("FieldUseForTempCodeOsRaster")

                # update the of the new field depending of the encoding set by the user
                for f in clone_layer.getFeatures():
                    counter += 1
                    self.progressBar.setValue(int(counter / total * 100))
                    currentValue = encoding[str(f[vector["burnInValue"]])]
                    if currentValue == '':
                        clone_layer.deleteFeature(f.id())
                    else:
                        clone_layer.changeAttributeValue(f.id(), index, currentValue, skipDefaultValues=True)

                self.logTextEdit.insertPlainText(self.translate('osraster', '\n->COMMITTING CHANGES\n'))

                clone_layer.commitChanges()
                file = clone_layer

            params = {'INPUT': file}

            if vector["valueType"] == 'Field':
                if vector["stringField"]:
                    params['FIELD'] = "FieldUseForTempCodeOsRaster"
                else:
                    params['FIELD'] = vector["burnInValue"]
            else:
                params['BURN'] = float(vector["burnInValue"])

            params['UNITS'] = 1

            resolution = self.resolutionLineEdit.text()
            params['WIDTH'] = float(resolution)
            params['HEIGHT'] = float(resolution)

            params['ALL_TOUCH'] = vector["allTouch"]

            params['NODATA'] = nodata

            params['EXTENT'] = extent

            params['DATA_TYPE'] = 2

            params['OUTPUT'] = QgsProcessing.TEMPORARY_OUTPUT

            self.logTextEdit.insertPlainText(self.translate('osraster', '\n->RASTERIZATION\n'))

            # rasterize and link the new output with the original pathfile
            exVectorTab[vector["file"]] = processing.run("graphab:rasterize", params, feedback=self.feedback)['OUTPUT']

            self.progressBar.setValue(100)

        return exVectorTab

    def openInfoWindow(self, title, message, index=None):
        QMessageBox.information(None, title, message)
        if index is not None:
            self.mTabWidget.setCurrentIndex(index)

    # --------------------------------------------------------------------------

    def subtractRasterFromData(self, file, group):
        self.logTextEdit.insertPlainText(self.translate('osraster', '\n->SUBTRACTING CODE RASTER\n'))

        # get nodata value and if not set use the default one
        nodata = self.nodataLineEdit.text()
        if self.isValidNoDataValue(nodata) and 0 <= int(float(nodata)) <= 65535:
            nodata = int(float(nodata))
        else:
            nodata = self.nodataDefault

        params = {
            "INPUT_RASTER": file,
            "RASTER_BAND": 1,
            "RANGE_BOUNDARIES": 2,
            "NO_DATA": nodata,
            "NODATA_FOR_MISSING": True,
            "DATA_TYPE": 2,
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT
        }

        # give as parameter a table with the raster code we want to keep
        replaceTab = []
        for code in group:
            replaceTab.extend([code, code, code])

        params["TABLE"] = replaceTab

        newRaster = processing.run("native:reclassifybytable", params, feedback=self.feedback)["OUTPUT"]
        self.progressBar.setValue(100)



        return newRaster

    def replaceRasterCodeWithEncoding(self, vectorToExVectorTab):
        self.logTextEdit.insertPlainText(self.translate('osraster', '\n->SEARCHING CODE TO REPLACE ON RASTER\n'))
        newRasterTab = {}

        # get nodata value and if not set use the default one
        nodata = self.nodataLineEdit.text()
        if self.isValidNoDataValue(nodata) and 0 <= int(float(nodata)) <= 65535:
            nodata = int(float(nodata))
        else:
            nodata = self.nodataDefault

        allElementTab = self.vectorTab + self.rasterTab
        for element in allElementTab:
            file = element["file"]

            # if the original file is a vector we use his raster file representation else we use this original filepath
            if file in vectorToExVectorTab:
                to = vectorToExVectorTab[file]

                # if the file use a string file as encoding
                # we don't need to replace code because this process was done in cloning vector from rasterizeAllVectors
                if element["stringField"]:
                    newRasterTab[file] = to
                    continue
            else:
                to = file

            # if the encoding is not the same as original we start replacing raster codes
            if not element["sameEncoding"]:
                self.logTextEdit.insertPlainText(
                    self.translate('osraster', '\n->REPLACING CODE RASTER : %s\n') % element["name"]
                )

                params = {
                    "INPUT_RASTER": to,
                    "RASTER_BAND": 1,
                    "RANGE_BOUNDARIES": 2,
                    "NO_DATA": nodata,
                    "NODATA_FOR_MISSING": False,
                    "DATA_TYPE": 2,
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT
                }
                replaceTab = []
                for (key, value) in element["encoding"].items():
                    # if value is nodata
                    if value == '':
                        replaceTab.extend([int(key), int(key), nodata])
                    elif int(key) != int(value):
                        replaceTab.extend([int(key), int(key), int(value)])

                params["TABLE"] = replaceTab
                to = processing.run("native:reclassifybytable", params, feedback=self.feedback)["OUTPUT"]
            newRasterTab[file] = to

        self.progressBar.setValue(100)
        return newRasterTab

    # --------------------------------------------------------------------------

    def fileSaveJSON(self):
        
        path, _filter = QFileDialog.getSaveFileName(
            self,
            self.translate('osraster', 'Save parameters File'),
            '',
            '*.json'
        )

        if path is not None and path != '':
            # If the file exists and is not a file we stop the process
            if os.path.exists(path) and not os.path.isfile(path):
                return

            # if the file doesn't exist we create a new one and save all parameters set by the user
            if not os.path.exists(path):
                with open(path, 'w') as file:
                    data = self.getJSONData()
                    file.write(data)
                return

            # Else we get his filepath and suffix
            fileInfo = QFileInfo(path)
            filePath = fileInfo.filePath()
            suffix = fileInfo.completeSuffix()

            # If that's not the suffix of a json file we stop the process
            if suffix != "json":
                return

            with open(filePath, 'w') as file:
                data = self.getJSONData()
                file.write(data)
            

    def fileImportJSON(self):
        path, _filter = QFileDialog.getOpenFileName(
            self,
            self.translate('osraster', 'Open parameters File'),
            '',
            '*.json'
        )

        if path is not None and path != '':
            # If the file doesn't exist or is not a file we stop the process
            if not os.path.exists(path) or not os.path.isfile(path):
                return

            # Else we get his filepath and suffix
            fileInfo = QFileInfo(path)
            filePath = fileInfo.filePath()
            suffix = fileInfo.completeSuffix()

            # If that's not the suffix of a json file we stop the process
            if suffix != "json":
                return

            #textFile = '' if NoneType not accepted
            with open(filePath, 'r') as file:
                textFile = file.read()

            self.initWithParameters(json.loads(textFile))


    @staticmethod 
    def _recreate_layer(layer: dict, is_raster: bool):
        providersVector = ['postgres', 'ogr', 'spatialite', 'mssql', 'wfs', 'oapif', 'delimitedtext', 'gpx', 'grass']
        providersRaster = ['gdal'] # might add other providers
        if is_raster:
            provider = [e for e in providersRaster if e in layer['layer']][0] # there should alaways be one and only one provider
            layer['layer'] = QgsRasterLayer(
                uri=layer['file'],
                baseName=layer['name'],
                providerType=provider
            )
        else:
            provider = [e for e in providersVector if e in layer['layer']][0] # there should always be one and only one provider
            layer['layer'] = QgsVectorLayer(
                path=layer['file'],
                baseName=layer['name'],
                providerLib=provider
            )

    def initWithParameters(self, data):
        # restore the default window
        self.clearAll(True)

        # get the raster table and initialize the rasterTableWidget
        for e in data['rasterTab']:
            self._recreate_layer(e, True)
        self.rasterTab = data["rasterTab"] 
        QgsProject.instance().addMapLayers([e['layer'] for e in data['rasterTab']])

        for raster in self.rasterTab:
            indexRaster = self.rasterTableWidget.rowCount()
            self.rasterTableWidget.setRowCount(indexRaster + 1)
            self.setRowRaster(raster, indexRaster, True)
        self.resizeAllFilePath(0, self.rasterTableWidget.columnWidth(0), 'raster')

        # get the vector table and initialize the vectorTableWidget
        for e in data['vectorTab']:
            self._recreate_layer(e, False)
            
        self.vectorTab = data["vectorTab"]
        QgsProject.instance().addMapLayers([e['layer'] for e in data['vectorTab']])
        
            
        for vector in self.vectorTab:
            indexVector = self.vectorTableWidget.rowCount()
            self.vectorTableWidget.setRowCount(indexVector + 1)
            self.setRowVector(vector, indexVector, True)
        self.resizeAllFilePath(0, self.vectorTableWidget.columnWidth(0), 'vector')

        # initialize the outputTab
        self.needOutputUpdate = data["needOutputUpdate"]
        if not self.needOutputUpdate:
            self.updateOutputTab()
            self.resolutionLineEdit.setText(data["resolution"])
            self.nodataLineEdit.setText(data["nodata"])
            #self.extentSelection.setOutputExtentFromLayer()
            #self.extentComboBox.setCurrentIndex(data["indexExtent"])
            self.extentSelection.setOriginalExtent(QgsRectangle(data["east"], data["south"], data["west"], data["north"]), QgsCoordinateReferenceSystem(data["crs"]))
            self.outputQgsProjectionSelectionWidget.setCrs(QgsCoordinateReferenceSystem(data["crs"]))

        self.outputQgsFileWidget.setFilePath(data["output"])

        # initialize the merge tab depending of the needOrderUpdate boolean
        self.orderTabIsUpdated = False
        self.needOrderUpdate = data["needOrderUpdate"]
        if not self.needOrderUpdate:
            self.orderTabPopulated = data["orderTabPopulated"]
            self.orderTableWidget.setRowCount(len(self.orderTabPopulated))
            counter = 0
            for order in self.orderTabPopulated:
                self.setRowOrder(counter, order["file"], order["oldCode"], order["newCode"])
                counter += 1

            self.resizeAllFilePath(0, self.orderTableWidget.columnWidth(0), 'order')

        self.orderTabIsUpdated = True

    def getJSONData(self):
        # Get a list of all paths selected by the user with the QgsFileWidget object
        filePathTab = self.outputQgsFileWidget.splitFilePaths(self.outputQgsFileWidget.filePath())
        filePathStr = ''
        if len(filePathTab) > 0:
            filePathStr = filePathTab[0]

        _rasterTab = self.rasterTab
        for i in range(len(_rasterTab)):
            _rasterTab[i]['layer'] = str(_rasterTab[i]['layer'])
        _vectorTab = self.vectorTab
        for i in range(len(_vectorTab)):
            _vectorTab[i]['layer'] = str(_vectorTab[i]['layer'])

        allData = {
            "rasterTab":  _rasterTab,
            "vectorTab": _vectorTab, 
            "needOrderUpdate": self.needOrderUpdate,
            "orderTabPopulated": self.orderTabPopulated,
            "needOutputUpdate": self.needOutputUpdate,
            "resolution": self.resolutionLineEdit.text(),
            "nodata": self.nodataLineEdit.text(),
            "north": self.extentSelection.outputExtent().yMaximum(),
            "south": self.extentSelection.outputExtent().yMinimum(),
            "east": self.extentSelection.outputExtent().xMinimum(),
            "west": self.extentSelection.outputExtent().xMaximum(),
#            "indexExtent": self.extentComboBox.currentIndex(),
            "crs": self.outputQgsProjectionSelectionWidget.crs().authid(),
            "output": filePathStr
        }

        return json.dumps(allData)

    # --------------------------------------------------------------------------

    def generateDifferentRaster(self, mergeRasterIndexTab):
        self.logTextEdit.insertPlainText(self.translate('osraster', '\n->STARTING SPLITTING RASTER BY GROUP OF CODE\n'))
        currentRasterTab = []
        currentGroup = []
        last = None
        total = len(self.orderTabPopulated)

        if total < 1:
            return None

        size = 0
        counter = 0

        # for each code in the order table we want to sort them by groups of same followings files
        for raster in self.orderTabPopulated:
            counter += 1

            # nodata code are skipped
            if raster["newCode"] == '':
                continue

            # if no filepath set we get the current filepath, code and it's original number of different encoding code
            if last is None:
                last = mergeRasterIndexTab[raster["file"]]
                currentGroup.append(raster["newCode"])
                size = raster["size"]
                continue

            # if the filepath is the same that the previous one we append the current code
            if last == mergeRasterIndexTab[raster["file"]]:
                currentGroup.append(raster["newCode"])
                continue

            # when we get here that's mean the current file isn't the same as the previous

            # if there is at least one code in the current group
            # we can subtract from the original file to get a new raster
            if len(currentGroup) > 0:
                if len(currentGroup) != size:
                    currentRasterTab.append(self.subtractRasterFromData(last, currentGroup))
                # if the number of code is equals to the original encoding size that's mean this is the complete raster
                # and we don't need to subtract it
                else:
                    currentRasterTab.append(last)

            self.progressBar.setValue(int(counter / total * 100))

            # set default parameters after subtracting
            currentGroup.clear()

            last = mergeRasterIndexTab[raster["file"]]
            currentGroup.append(raster["newCode"])
            size = raster["size"]

        if len(currentGroup) > 0:
            if len(currentGroup) != size:
                currentRasterTab.append(self.subtractRasterFromData(last, currentGroup))
            else:
                currentRasterTab.append(last)

        self.progressBar.setValue(100)

        return currentRasterTab

    def mergeRasters(self):
        if self.needOrderUpdate:
            self.openInfoWindow(
                self.translate('osraster', "ERROR - merge"),
                self.translate('osraster', "The merge tab isn't initialized."),
                1
            )
            return

        if self.needOutputUpdate:
            self.openInfoWindow(
                self.translate('osraster', "ERROR - output"),
                self.translate('osraster', "The output tab isn't initialized."),
                2
            )
            return

        resolution = self.resolutionLineEdit.text()
        if not self.isFloat(resolution):
            self.openInfoWindow(
                self.translate('osraster', "ERROR - resolution"),
                self.translate('osraster', "Enter a correct resolution value."),
                2
            )
            return

        rect = self.extentSelection.outputExtent()

        if rect.isEmpty():
            self.openInfoWindow(
                self.translate('osraster', "ERROR - extent"),
                self.translate('osraster', "Enter a correct extent values."),
                2
            )
            return

        pixelSize = self.numberLabel.text()
        if not self.isFloat(pixelSize) or float(pixelSize) <= 0:
            self.openInfoWindow(
                self.translate('osraster', "ERROR - extent"),
                self.translate('osraster', "The output can't have a size of 0 pixels."),
                2
            )
            return

        crs = self.outputQgsProjectionSelectionWidget.crs().authid()
        if crs == '' or crs is None:
            self.openInfoWindow(
                self.translate('osraster', "ERROR - output"),
                self.translate('osraster', "There is no CRS selected."),
                2
            )
            return

        nodata = self.nodataLineEdit.text()
        if nodata != '':
            if not self.isValidNoDataValue(nodata):
                self.openInfoWindow(
                    self.translate('osraster', "ERROR - output"),
                    self.translate('osraster', "The nodata value is incorrect."),
                    2
                )
                return
            elif int(float(nodata)) < 0:
                self.openInfoWindow(
                    self.translate('osraster', "ERROR - output"),
                    self.translate('osraster', "The nodata value can't be negative."),
                    2
                )
                return
            elif int(float(nodata)) > 65535:
                self.openInfoWindow(
                    self.translate('osraster', "ERROR - output"),
                    self.translate('osraster', "The nodata value can't be greater than 65 535."),
                    2
                )
                return

        self.logTextEdit.clear()
        self.mTabWidget.setCurrentIndex(3)

        self.logTextEdit.insertPlainText(self.translate('osraster', '->BEGINNING OF THE PROCESS\n\n'))

        vectorToExVectorTab = self.rasterizeAllVector(crs)

        fileToNewRaster = self.replaceRasterCodeWithEncoding(vectorToExVectorTab)

        self.logTextEdit.insertPlainText(self.translate('osraster', '\n->SEARCHING FOR REPROJECTION ON RASTER\n'))
        for (old, new_) in fileToNewRaster.items():
            layer = QgsRasterLayer(new_)

            crsSource = layer.crs().authid()
            extentSource = layer.extent().toString()
            extent = rect.toString()
            widthSource = layer.rasterUnitsPerPixelX()
            heightSource = layer.rasterUnitsPerPixelY()

            differentCrs = crsSource != crs
            differentExtent = extentSource != extent
            differentResolution = widthSource != float(resolution) or heightSource != float(resolution)

            if differentCrs or differentExtent or differentResolution:
                self.logTextEdit.insertPlainText(self.translate('osraster', '\n->REPROJECTION RASTER\n'))
                new_ = self.reprojectRasterWithCRS(layer, crsSource, crs)
                fileToNewRaster[old] = new_

        rastersTab = self.generateDifferentRaster(fileToNewRaster)
        if rastersTab is None:
            return

        # to respect the correct merge order
        rastersTab.reverse()

        """     
        tempRaster = []
        for r in rastersTab:
            self.logTextEdit.insertPlainText(self.translate('osraster', '\n->VIRTUALIZATION\n'))
            params_virtual = {
                "INPUT": r,
                "RESOLUTION": 1,
                "SEPARATE": False,
                "PROJ_DIFFERENCE": False,
                "ADD_ALPHA": False,
                "RESAMPLING": 0,
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT
            }

            if self.isValidNoDataValue(nodata) and 0 <= int(float(nodata)) <= 65535:
                params_virtual["SRC_NODATA"] = int(float(nodata))
            else:
                params_virtual["SRC_NODATA"] = self.nodataDefault

            tempRaster.append(processing.run("gdal:buildvirtualraster", params_virtual, feedback=self.feedback)['OUTPUT'])

        print(tempRaster)
        rastersTab = tempRaster
        """

        # TODO - skip merge process if only 1 raster ?
        if len(rastersTab) > 0:
            name = 'Merge'

            self.logTextEdit.insertPlainText(self.translate('osraster', '\n->MERGE\n'))
            params = {
                'INPUT': rastersTab,
                'DATA_TYPE': 2
            }

            # Get a list of all paths selected by the user with the QgsFileWidget object
            filePathTab = self.outputQgsFileWidget.splitFilePaths(self.outputQgsFileWidget.filePath())
            if len(filePathTab) > 0:
                fileInfo = QFileInfo(filePathTab[0])
                filePath = fileInfo.filePath()
                name = fileInfo.baseName()

                # if the file path set already exists to rewrite it we need to delete it
                if os.path.exists(filePath):
                    try:
                        os.remove(filePath)
                    except OSError as e:
                        print(self.translate('osraster', "Error: %s - %s.") % (e.filename, e.strerror))

                params['OUTPUT'] = filePath
            else:
                params['OUTPUT'] = QgsProcessing.TEMPORARY_OUTPUT

            if self.isValidNoDataValue(nodata) and 0 <= int(float(nodata)) <= 65535:
                # params['NODATA_INPUT'] = int(float(nodata))
                params['NODATA_OUTPUT'] = int(float(nodata))
            else:
                # params['NODATA_INPUT'] = self.nodataDefault
                params['NODATA_OUTPUT'] = self.nodataDefault

            """
            # don't know if this is necessary yet (causes bugs without but different too with)
            layers = QgsProject.instance().layerTreeRoot().findLayers()
            # gdal:merge input should be a layer, so if current input is not a layer, it is transformed into a layer. 
            for (e, i) in enumerate(params['INPUT']):
                if type(e) != QgsRasterLayer:
                    try:
                        params['INPUT'][i] = layers[layers.index(e)]
                    except ValueError :
                        params['INPUT'][i] = QgsRasterLayer(e)
            """
            
            raster = processing.run("gdal:merge", params, feedback=self.feedback)['OUTPUT']
            self.progressBar.setValue(100)

            # load the merge file in the current QGIS window
            vlayer = QgsRasterLayer(raster, name)
            QgsProject.instance().addMapLayer(vlayer)

    # --------------------------------------------------------------------------

    def reprojectVectorWithCRS(self, file, crs):
        params = {
            "INPUT": file,
            "TARGET_CRS": QgsCoordinateReferenceSystem(crs),
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT
        }
        return processing.run("native:reprojectlayer", params, feedback=self.feedback)['OUTPUT']

    def reprojectRasterWithCRS(self, file, crsSource, crsTarget):
        params = {
            "INPUT": file,
            "SOURCE_CRS": QgsCoordinateReferenceSystem(crsSource),
            "TARGET_CRS": QgsCoordinateReferenceSystem(crsTarget),
            "RESAMPLING": 0,
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            "DATA_TYPE": 3,
            "TARGET_EXTENT_CRS": QgsCoordinateReferenceSystem(crsTarget)
        }

        """nodata = self.nodataLineEdit.text()
        if self.isFloat(nodata):
            params['NODATA'] = float(nodata)"""

        resolution = self.resolutionLineEdit.text()
        params["TARGET_RESOLUTION"] = resolution

        params["TARGET_EXTENT"] = self.extentSelection.outputExtent()

        return processing.run("gdal:warpreproject", params, feedback=self.feedback)['OUTPUT']

    # --------------------------------------------------------------------------

    def updateNeeded(self):
        self.needOutputUpdate = True
        self.needOrderUpdate = True
        self.progressBar.setValue(0)

    # --------------------------------------------------------------------------

    def clearAll(self, jsonImport=False):
        if not jsonImport:
            reply = QMessageBox.question(
                None,
                self.translate('osraster', "Reset window ?"),
                self.translate('osraster', "Do you want to clear all the window ?"),
                QMessageBox.Yes,
                QMessageBox.No
            )

            if reply == QMessageBox.No:
                return

        self.rasterTab = []
        self.rasterTableWidget.setRowCount(0)

        self.vectorTab = []
        self.vectorTableWidget.setRowCount(0)

        self.orderTabPopulated = []
        self.orderTableWidget.setRowCount(0)

        self.extentTab = []

        self.resolutionLineEdit.setText("10.00000")
        self.nodataLineEdit.clear()


        self.RasterDialog = None
        self.VectorDialog = None

        self.needOrderUpdate = False
        self.needOutputUpdate = False

        self.orderTabIsUpdated = False

        self.logTextEdit.clear()

        self.numberLabel.setText("0")

        self.mTabWidget.setCurrentIndex(0)

        self.progressBar.setValue(0)

        self.outputQgsFileWidget.setFilePath('')

    # --------------------------------------------------------------------------

    @staticmethod
    def isFloat(string):
        try:
            float(string)
            return True
        except ValueError:
            return False

    @staticmethod
    def isInteger(string):
        try:
            int(string)
            return True
        except ValueError:
            return False

    @staticmethod
    def isValidNoDataValue(nodata):
        try:
            return int(float(nodata)) == float(nodata)
        except ValueError:
            return False

    # --------------------------------------------------------------------------

    # Processing feedback
    def progress_changed(self, progress):
        self.progressBar.setValue(int(progress))

    # --------------------------------------------------------------------------

    # function use to manage a drop event on the orderTableWidget
    def rowDropped(self, event):
        # prevent the call of an other event
        self.orderTabIsUpdated = False

        # get all uniques row selected from the orderTableWidget and sort them (they are indexes)
        allSelectedRow = self.orderTableWidget.selectionModel().selectedRows()
        allSelectedRow = [uniqueRow.row() for uniqueRow in allSelectedRow]
        allSelectedRow.sort()

        # get the row where the drop was
        destination = self.orderTableWidget.rowAt(event.pos().y())
        if destination < 0:
            event.accept()
            self.orderTabIsUpdated = True
            return

        # verify if rows aren't already in the good place
        lastRow = allSelectedRow[-1]
        firstRow = allSelectedRow[0]
        if (lastRow - firstRow) == (len(allSelectedRow) - 1) and firstRow <= destination <= lastRow:
            event.accept()
            self.orderTabIsUpdated = True
            return

        # if not start we sort them in 2 groups to know which row have to move down or up depending of drop position
        tabToMoveUp = []
        tabToMoveDown = []
        for row in allSelectedRow:
            if destination < row:
                tabToMoveUp.append(row)
            else:
                tabToMoveDown.append(row)

        self.orderTableWidget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)

        # we start by moving down row one by one then we moving up one by one
        index = destination
        tabToMoveDown.reverse()
        for selectedRow in tabToMoveDown:
            if (index + 1) - selectedRow == 0:
                index -= 1
                continue
            self.moveDown('order', selectedRow, (index + 1) - selectedRow)
            index -= 1

        index = 0
        for selectedRow in tabToMoveUp:
            if (selectedRow - destination) - index == 0:
                index += 1
                continue
            if len(tabToMoveDown) == 0:
                self.moveUp('order', selectedRow, (selectedRow - destination) - index)
            else:
                self.moveUp('order', selectedRow, (selectedRow - (destination + (len(tabToMoveDown) - 1))) - index)
            index += 1

        self.orderTableWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
        self.orderTabIsUpdated = True
        event.accept()

    # --------------------------------------------------------------------------

    def moveCursor(self):
        # set the cursor at the end of the logTextEdit object
        cursorEnd = self.logTextEdit.textCursor()
        cursorEnd.movePosition(QTextCursor.End)
        self.logTextEdit.setTextCursor(cursorEnd)
        self.logTextEdit.ensureCursorVisible()

    # --------------------------------------------------------------------------

    @staticmethod
    def translate(context, string):
        return QCoreApplication.translate(context, string)
