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

from qgis._core import QgsMapLayerProxyModel

import processing

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

from qgis.core import QgsVectorLayer, QgsFieldProxyModel, QgsProcessing, QgsProject

from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtCore import QFileInfo, QCoreApplication

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


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

        # initialize connections between actions on window elements and functions
        self.mapLayerComboBox.setFilters(QgsMapLayerProxyModel.VectorLayer)
        self.fileQgsFileWidget.fileChanged.connect(self.select_vector)
        self.mapLayerComboBox.currentIndexChanged.connect(self.select_layer)
        self.typeButtonGroup.buttonToggled.connect(self.showSelectedValueTypeInput)
        self.bufferCheckBox.toggled.connect(self.showBufferInput)

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

        self.fileVector = None

        self.vectorLayer = None
        self.fileName = None
        self.fileInfo = None

        self.valueType = 'Field'
        self.burnInValue = None
        self.encoding = None

        self.allTouch = False

        self.bufferValue = None

        self.alreadyVector = False

        self.stringField = False

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

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

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

        # if we edit a file already loaded we disable the widget that able you to select a new file
        if vector is not None:
            self.alreadyVector = True
            self.fileQgsFileWidget.setEnabled(False)
            self.mapLayerComboBox.setEnabled(False)
            self.fileLabel.setEnabled(False)

            fileInfo = QFileInfo(vector["file"])
            filePath = fileInfo.filePath()
            baseName = fileInfo.baseName()
            

            self.fileQgsFileWidget.setFilePath(filePath)

            
            # Load the vector layer file and stock it in a global variable
            if 'dbname' in vector['file']:
                self.select_layer(to_update=vector)
            else:    
                vectorLayer = QgsVectorLayer(filePath, baseName)
                self.vectorLayer = vectorLayer
            
            self.fileInfo = fileInfo
            self.fileName = baseName
            self.fileVector = filePath

            self.addAllFieldsName(self.vectorLayer)

            self.initInputDialogFromVector(vector)

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

                if self.fieldRadioButton.isChecked():
                    self.valueType = 'Field'
                    self.burnInValue = self.fieldComboBox.currentField()

                    if self.burnInValue == '':
                        QMessageBox.information(
                            None,
                            self.translate('osraster_vector', "ERROR : Vector field value"),
                            self.translate('osraster_vector', "The field value set isn't valid.")
                        )
                        continue

                    int_field = [
                        field.type() for field in self.vectorLayer.fields() if field.name() == self.burnInValue
                    ][0]

                    self.stringField = int_field == 10
                    self.encoding = self.getEncodingFromVector(self.vectorLayer, self.burnInValue, int_field)
                    if self.encoding == 'not_integer':
                        QMessageBox.information(
                            None,
                            self.translate('osraster_vector', "ERROR : Vector unique codes"),
                            self.translate(
                                'osraster_vector',
                                "Encoding can't use float value if they can't represent an integer."
                            )
                        )
                        continue

                    elif self.encoding == 'not_positive':
                        QMessageBox.information(
                            None,
                            self.translate('osraster_vector', "ERROR : Vector unique codes"),
                            self.translate('osraster_vector', "A code value can't be negative.")
                        )
                        continue

                    elif self.encoding == 'too_big':
                        QMessageBox.information(
                            None,
                            self.translate('osraster_vector', "ERROR : Vector encoding value"),
                            self.translate('osraster_vector', "A code value can't be greater than 65 535.")
                        )
                        break

                    if len(self.encoding.items()) > self.maxUniquesCodePossible:
                        QMessageBox.information(
                            None,
                            self.translate('osraster_vector', "ERROR : Vector unique codes"),
                            self.translate(
                                'osraster_vector',
                                "There is too much unique code values. (MAX=%d)"
                            ) % self.maxUniquesCodePossible
                        )
                        continue

                else:
                    self.stringField = False
                    self.valueType = 'Fixed'
                    self.burnInValue = self.valueLineEdit.text()

                    if not self.isInteger(self.burnInValue):
                        QMessageBox.information(
                            None,
                            self.translate('osraster_vector', "ERROR : Vector burn-in value"),
                            self.translate('osraster_vector', "The burn-in value set isn't valid.")
                        )
                        continue

                    elif int(float(self.burnInValue)) < 0:
                        QMessageBox.information(
                            None,
                            self.translate('osraster_vector', "ERROR : Vector burn-in value"),
                            self.translate('osraster_vector', "The burn-in value can't be negative.")
                        )
                        continue

                    elif int(float(self.burnInValue)) > 65535:
                        QMessageBox.information(
                            None,
                            self.translate('osraster_vector', "ERROR : Vector burn-in value"),
                            self.translate('osraster_vector', "The burn-in value can't be greater than 65 535.")
                        )
                        continue

                    else:
                        # transform the value in integer
                        self.burnInValue = int(float(self.burnInValue))

                    self.encoding = {self.burnInValue: self.burnInValue}

                self.allTouch = self.allTouchCheckBox.isChecked()

                if self.bufferCheckBox.isChecked():
                    self.bufferValue = self.bufferLineEdit.text()

                    if not self.isFloat(self.bufferValue):
                        QMessageBox.information(
                            None,
                            self.translate('osraster_vector', "ERROR : Vector buffer value"),
                        )
                        continue
                    else:
                        self.bufferValue = float(self.bufferValue)
                else:
                    self.bufferValue = None

                size = len(set(self.encoding.values()))
                if size == 0:
                    QMessageBox.information(
                        None,
                        self.translate('osraster_vector', "ERROR : Vector encoding size"),
                        self.translate('osraster_vector', "A vector can't have 0 values.")
                    )
                    continue

                # add the layer to the layermap if it was coming from a file
                root = QgsProject.instance().layerTreeRoot()
                if root.findLayer(self.vectorLayer.id()) is None :
                    QgsProject.instance().addMapLayer(self.vectorLayer)
                    # hide the layer
                    root.findLayer(self.vectorLayer.id()).setItemVisibilityChecked(False)

                # Return every parameters needed
                return {
                    "name": self.vectorLayer.name(),
                    "layer": self.vectorLayer,
                    "file": self.vectorLayer.source(),
                    "valueType": self.valueType,
                    "burnInValue": self.burnInValue,
                    "allTouch": self.allTouch,
                    "bufferValue": self.bufferValue,
                    "encoding": self.encoding,
                    "sameEncoding": True,
                    "nodata": False,
                    "size": len(self.encoding.values()),
                    "stringField": self.stringField
                }

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

        return None

    def initInputDialogFromVector(self, vector):
        if vector["valueType"] == 'Field':
            self.fieldComboBox.setField(vector["burnInValue"])
            self.fieldRadioButton.setChecked(True)
        else:
            self.valueLineEdit.setText(str(vector["burnInValue"]))
            self.valueRadioButton.setChecked(True)

        self.showSelectedValueTypeInput()

        self.allTouchCheckBox.setChecked(vector["allTouch"])

        if vector["bufferValue"] is not None:
            self.bufferCheckBox.setChecked(True)
            self.bufferLineEdit.setText(str(vector["bufferValue"]))
            self.showBufferInput()

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

    def select_layer(self, to_update: dict = None):
        """Get the selected layer"""
        if to_update is not None and type(to_update) == dict:
            index = 0
            while index < len(QgsProject.instance().mapLayers().items()):
                layer = self.mapLayerComboBox.layer(index)
                if layer is not None:
                    if to_update['layer'].name() == layer.name():
                        self.mapLayerComboBox.setCurrentIndex(index)
                index += 1
        
        layer = self.mapLayerComboBox.currentLayer()
        self.vectorLayer = layer

        self.addAllFieldsName(layer)

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

    def select_vector(self):
        """Get the path of a file from the QgsFileWidget and try to load it if it exists"""
        if self.alreadyVector:
            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 vector layer and stock it in a global variable
        vectorLayer = QgsVectorLayer(filePath, baseName)
        if not vectorLayer.isValid():
            self.resetNoFile()
            QMessageBox.information(
                None,
                self.translate('osraster_vector', "ERROR : Vector file"),
                self.translate('osraster_vector', "The file isn't valid.")
            )
            return

        self.mapLayerComboBox.setAdditionalLayers([vectorLayer])
        self.mapLayerComboBox.setLayer(vectorLayer)
        self.vectorLayer = vectorLayer
        self.fileInfo = fileInfo
        self.fileName = baseName
        self.fileVector = filePath

        self.addAllFieldsName(vectorLayer)

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

    def showSelectedValueTypeInput(self):
        self.fieldComboBox.setEnabled(self.fieldRadioButton.isChecked())
        self.valueLineEdit.setEnabled(self.valueRadioButton.isChecked())

    def showBufferInput(self):
        self.bufferLineEdit.setEnabled(self.bufferCheckBox.isChecked())

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

    def addAllFieldsName(self, vector=None):
        if vector is None:
            self.fieldComboBox.clear()
            self.fieldComboBox.setEnabled(False)
        else:
            self.fieldComboBox.setLayer(vector)

            allAvailableFields = vector.fields()

            if len(allAvailableFields) > 0:
                self.fieldComboBox.setField(allAvailableFields[0].name())
                self.fieldComboBox.setEnabled(self.fieldRadioButton.isChecked())
                self.fieldRadioButton.setEnabled(True)
            else:
                self.valueRadioButton.setChecked(True)
                self.fieldRadioButton.setEnabled(False)
                self.fieldComboBox.setEnabled(False)
                #self.fieldLabel.setEnabled(False)


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

    def resetNoFile(self):
        self.addAllFieldsName()
        self.vectorLayer = None
        self.fileInfo = None
        self.fileName = None
        self.fileVector = None

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

    def getEncodingFromVector(self, layer, field, fieldType):
        encoding = {}
        params = {
            "INPUT": layer,
            "FIELDS": field,
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT
        }

        stringValues = processing.run("qgis:listuniquevalues", params, feedback=self.feedback)["UNIQUE_VALUES"]
        tabValues = stringValues.split(';')

        if fieldType == 10:
            counter = 1
            for code in tabValues:
                encoding[code] = counter
                counter += 1

        else:
            for code in tabValues:
                if not self.isInteger(code):
                    return 'not_integer'
                elif int(float(code)) < 0:
                    return 'not_positive'
                elif int(float(code)) > 65535:
                    return 'too_big'
                else:
                    encoding[str(code)] = int(float(code))

        return encoding

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

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

    @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)
