# -*- 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 processing

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

from qgis._core import QgsMapLayerProxyModel
from qgis.core import QgsRasterLayer, QgsProcessing, QgsVectorLayer, QgsProject, QgsProcessingParameterDefinition, QgsLayerTree

from qgis.gui import QgsProcessingMapLayerComboBox

from PyQt5.QtCore import QFileInfo, Qt, QCoreApplication

from PyQt5.QtWidgets import QTableWidgetItem, QMessageBox, QDialog

# 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_option_raster_window.ui'))


class RasterDialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, feedback, maxCode):
        """Constructor."""
        super(RasterDialog, 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


        #self.select_layer_widget = QgsProcessingMapLayerComboBox(QgsProcessingParameterDefinition("raster_dialog"))
        

        # initialize connections between actions on window elements and functions
        self.mapLayerComboBox.setFilters(QgsMapLayerProxyModel.RasterLayer)
        self.fileQgsFileWidget.fileChanged.connect(self.select_raster)
        self.mapLayerComboBox.currentIndexChanged.connect(self.select_layer)

        # initialize globals variables
        self.feedback = feedback
        self.maxUniquesCodePossible = maxCode

        self.fileRaster = None

        self.rasterLayer = None
        self.fileName = None
        self.fileInfo = None

        self.encoding = None

        self.alreadyRaster = False

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

        if self.mapLayerComboBox.currentLayer() is not None:
            self.select_layer()

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

    def showDialog(self, raster=None, vector=False):
        """Initialize basic window and waiting for any action of the user"""

        if raster is not None:
            self.alreadyRaster = True
            self.fileQgsFileWidget.setEnabled(False)
            self.mapLayerComboBox.setEnabled(False)
            self.fileLabel.setEnabled(False)
            fileInfo = QFileInfo(raster["file"])
            filePath = fileInfo.filePath()
            baseName = fileInfo.baseName()

            self.fileQgsFileWidget.setFilePath(filePath)

            if vector:
                # Load the vector layer file and stock it in a global variable to recode encoding
                rasterLayer = QgsVectorLayer(filePath, baseName)
            else:
                # Load the raster layer file and stock it in a global variable
                rasterLayer = QgsRasterLayer(filePath, baseName)

            self.rasterLayer = rasterLayer
            self.fileInfo = fileInfo
            self.fileName = baseName
            self.fileRaster = filePath

            self.populateEncodingTabFromDictionary(raster["encoding"])
            self.encodingTableWidget.setEnabled(True)
            self.encodingLabel.setEnabled(True)

        while self.exec_():
            # See if OK was pressed and wanted parameters are set
            
            if self.rasterLayer is not None :

                nodata = False
                sameEncoding = True
                self.encoding = {}

                # Create a dict of encoding with the qTableWidget
                for row in range(self.encodingTableWidget.rowCount()):
                    # Get items from the current row from the first and second column
                    _oldCode = self.encodingTableWidget.item(row, 0)
                    _newCode = self.encodingTableWidget.item(row, 1)
                    # If they exist get the text in it, compare them and add them to a dict
                    if _oldCode and _newCode:
                        oldCode = self.encodingTableWidget.item(row, 0).text()
                        newCode = self.encodingTableWidget.item(row, 1).text()

                        if self.isInteger(oldCode):
                            oldCode = int(float(oldCode))

                        if newCode == '':
                            nodata = True
                            sameEncoding = False

                        else:
                            if not self.isInteger(newCode):
                                QMessageBox.information(
                                    None,
                                    self.translate('osraster_raster', "ERROR : Raster encoding value"),
                                    self.translate('osraster_raster', "A code value set isn't valid.")
                                )
                                break
                            elif int(float(newCode)) < 0:
                                QMessageBox.information(
                                    None,
                                    self.translate('osraster_raster', "ERROR : Raster encoding value"),
                                    self.translate('osraster_raster', "A code value can't be negative.")
                                )
                                break
                            elif int(float(newCode)) > 65535:
                                QMessageBox.information(
                                    None,
                                    self.translate('osraster_raster', "ERROR : Raster encoding value"),
                                    self.translate('osraster_raster', "A code value can't be greater than 65 535.")
                                )
                                break

                            newCode = int(float(newCode))

                            if oldCode != newCode:
                                sameEncoding = False

                        self.encoding[oldCode] = newCode

                    else:
                        QMessageBox.information(
                            None,
                            self.translate('osraster_raster', "ERROR : Raster encoding value"),
                            self.translate('osraster_raster', "A code value set isn't valid.")
                        )
                        break

                else:

                    size = len(set(self.encoding.values()))
                    if nodata:
                        size -= 1

                    if size == 0:
                        QMessageBox.information(
                            None,
                            self.translate('osraster_raster', "ERROR : Raster encoding size"),
                            self.translate('osraster_raster', "A raster can't have 0 values.")
                        )
                        continue

                    # add the layer to the layermap if it was coming from a file
                    root: QgsLayerTree = QgsProject.instance().layerTreeRoot()
                    if root.findLayer(self.rasterLayer.id()) is None:
                        QgsProject.instance().addMapLayer(self.rasterLayer)
                        # hide the layer
                        root.findLayer(self.rasterLayer.id()).setItemVisibilityChecked(False)
                    
                    # Return every parameters needed
                    # If the encoding didn't change it's unnecessary to send the encoding tab so we send None
                    return {
                        "name": self.rasterLayer.name(),
                        "layer": self.rasterLayer,
                        "file": self.rasterLayer.source(),
                        "encoding": self.encoding,
                        "sameEncoding": sameEncoding,
                        "nodata": nodata,
                        "size": len(self.encoding.values())
                    }

            else:
                QMessageBox.information(
                    None,
                    self.translate('osraster_raster', "ERROR : Raster file value"),
                    self.translate('osraster_raster', "There is no file is set.")
                )
                continue

        return None

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

    def select_layer(self):
        """Get the selected layer"""
        layer = self.mapLayerComboBox.currentLayer()
        self.rasterLayer = layer

        self.populateEncodingTabFromRaster(self.rasterLayer.source())
        self.encodingTableWidget.setEnabled(True)
        self.encodingLabel.setEnabled(True)

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

    def select_raster(self):
        """Get the path of a file from the QgsFileWidget and try to load it if it exists"""
        if self.alreadyRaster:
            return

        # Get a list of all paths selected by the user with the QgsFileWidget object
        filePathTab = self.fileQgsFileWidget.splitFilePaths(self.fileQgsFileWidget.filePath())

        # If there is no paths selected we stop the process
        if len(filePathTab) < 1:
            self.resetNoFile()
            return

        # Else we get the first path of the list
        filePathStr = filePathTab[0]

        # If there is no existing file with the current path we stop the process
        if not os.path.exists(filePathStr) or not os.path.isfile(filePathStr):
            self.resetNoFile()
            return

        # Else we get his name, filepath and suffix
        fileInfo = QFileInfo(filePathStr)
        filePath = fileInfo.filePath()
        baseName = fileInfo.baseName()

        # Load the file as a raster layer and stock it in a global variable
        rasterLayer = QgsRasterLayer(filePath, baseName)
        if not rasterLayer.isValid():
            self.resetNoFile()
            QMessageBox.information(
                None,
                self.translate('osraster_raster', "ERROR : Raster file"),
                self.translate('osraster_raster', "The file isn't valid.")
            )
            return

        self.mapLayerComboBox.setAdditionalLayers([rasterLayer])
        self.mapLayerComboBox.setLayer(rasterLayer)
        self.rasterLayer = rasterLayer
        self.fileInfo = fileInfo
        self.fileName = baseName
        self.fileRaster = filePath

        self.populateEncodingTabFromRaster(filePath)
        self.encodingTableWidget.setEnabled(True)
        self.encodingLabel.setEnabled(True)

        

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

    def populateEncodingTabFromDictionary(self, encoding):
        """Initialize the encode tab with an encoding dictionary"""

        self.encodingTableWidget.setRowCount(0)

        # Add a number of row depending of the number of keys from the dictionary
        self.encodingTableWidget.setRowCount(len(encoding.keys()))

        # Fill lines
        counter = 0
        for (key, value) in encoding.items():
            item = QTableWidgetItem(str(key))
            item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.encodingTableWidget.setItem(counter, 0, item)
            self.encodingTableWidget.setItem(counter, 1, QTableWidgetItem(str(value)))
            counter += 1

    def populateEncodingTabFromRaster(self, raster=None):
        """Initialize the encode tab with the selected raster, if raster is None it clear the tab"""
        self.encodingTableWidget.setRowCount(0)

        if raster is None:
            return

        unique_values = self.getUniquesValuesFromRaster(raster)
        if len(unique_values) > self.maxUniquesCodePossible:
            QMessageBox.information(
                None,
                self.translate('osraster_raster', "ERROR : Raster unique codes"),
                self.translate('osraster_raster', "This raster has been ignored because there is too much unique code values. (MAX=%d)") % self.maxUniquesCodePossible
            )
            self.resetNoFile()
            return

        # Add a number of row depending of the number of unique values
        self.encodingTableWidget.setRowCount(len(unique_values))

        # Fill lines
        counter = 0
        for code in unique_values:
            intCode = int(float(code))
            item = QTableWidgetItem(str(intCode))
            item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.encodingTableWidget.setItem(counter, 0, item)
            self.encodingTableWidget.setItem(counter, 1, QTableWidgetItem(str(intCode)))
            counter += 1

    def getUniquesValuesFromRaster(self, raster):
        params = {
            "INPUT": raster,
            "BAND": 1,
            "OUTPUT_TABLE": QgsProcessing.TEMPORARY_OUTPUT
        }
        vectorLayer = processing.run("native:rasterlayeruniquevaluesreport", params, feedback=self.feedback)["OUTPUT_TABLE"]

        params = {
            "INPUT": vectorLayer,
            "FIELDS": 'value',
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT
        }
        stringValues = processing.run("qgis:listuniquevalues", params, feedback=self.feedback)["UNIQUE_VALUES"]
        tabValues = stringValues.split(';')
        if tabValues.count("\"") == 0: #for a strange reason, when there is no result, tabValues == ["\""]
            return sorted(tabValues, key=lambda x: int(float(x)))
        return []

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

    def resetNoFile(self):
        self.encodingTableWidget.setEnabled(False)
        self.encodingLabel.setEnabled(False)
        self.populateEncodingTabFromRaster()
        self.rasterLayer = None
        self.fileInfo = None
        self.fileName = None
        self.fileRaster = None

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

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

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

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