# -*- coding: utf-8 -*-
"""
/***************************************************************************
 DEMDrainageAnalyses
                                 A QGIS plugin
 This plugin performs specific DEM analyses on drainage sites.
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2020-07-01
        git sha              : $Format:%H$
        copyright            : (C) 2020 by FALASY Anamelechi
        email                : fvw.services@gmail.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 errno
import os.path
import sys
import csv

from qgis.PyQt.QtCore import *
from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication
from qgis.PyQt.QtGui import *
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import *
from qgis.PyQt.QtWidgets import QAction
from qgis.core import *
from qgis.core import QgsProject, QgsProcessingException
from qgis.utils import *

from qgis import processing
import os

# Initialize Qt resources from file resources.py
from .resources import *
# Import the code for the dialog
from .DEM_drainage_analyses_dialog import DEMDrainageAnalysesDialog


# from processing.algs.saga

class DEMDrainageAnalyses:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            'DEMDrainageAnalyses_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Create the dialog (after translation) and keep reference
        self.dlg = DEMDrainageAnalysesDialog()

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&DEM Drainage Analyses')
        # TODO: We are going to let the user set this up in a future iteration
        self.toolbar = self.iface.addToolBar(u'DEMDrainageAnalyses')
        self.toolbar.setObjectName(u'DEMDrainageAnalyses')

        self.dlg.lineEdit.clear()
        self.initFolder();
        self.dlg.pushButton.clicked.connect(self.selectOutputFile);

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('DEMDrainageAnalyses', message)

    def add_action(
            self,
            icon_path,
            text,
            callback,
            enabled_flag=True,
            add_to_menu=True,
            add_to_toolbar=True,
            status_tip=None,
            whats_this=None,
            parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            # self.toolbar.addAction(action)
            self.iface.addToolBarIcon(action)

        if add_to_menu:
            self.iface.addPluginToMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/DEM_drainage_analyses/icon.png'
        self.add_action(
            icon_path,
            text=self.tr(u'performs DEM analyses on drainage sites'),
            callback=self.run,
            parent=self.iface.mainWindow())

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(
                self.tr(u'&DEM Drainage Analyses'),
                action)
            self.iface.removeToolBarIcon(action)
        # # remove the toolbar
        # del self.toolbar

    # def getVectorLayer(self):
        # # get layer from combobox
        # self.layer = self.dlg.mMapLayerComboBox.currentLayer()
        
    # def getRasterLayer(self):
        # # get layer from combobox
        # self.layer = self.dlg.mMapLayerComboBox_2.currentLayer()
    
    def initFolder(self):
        path_project = QgsProject.instance().fileName()
        path_project = path_project[:path_project.rfind("/"):]

        self.folderName = path_project

        self.dlg.lineEdit.setText(self.folderName);

    def selectOutputFile(self):
        folderTmp = QFileDialog.getExistingDirectory(self.dlg,
                                                     self.tr("Select output folder "), self.folderName)

        if folderTmp != "":
            self.folderName = folderTmp

        self.dlg.lineEdit.setText(self.folderName);

    def isFileOpened(self, file_path):
        if os.path.exists(file_path):
            try:
                os.rename(file_path, file_path + "_")
                os.rename(file_path + "_", file_path)
                return False
            except OSError as e:
                return True

    def run(self):
        """Run method that performs all the real work"""
        self.dlg.comboBox.clear()
        layers = QgsProject.instance().mapLayers().values()
        # fill selection combo, only polygon layers
        n = 0
        for layer in layers:
            if layer.type() == QgsMapLayer.VectorLayer and \
                    layer.geometryType() == QgsWkbTypes.PolygonGeometry:
                self.dlg.comboBox.addItem(layer.name(), layer)
                n += 1

        if n == 0:  # no polygon layer
            iface.messageBar().pushMessage(self.tr("Warning"),
                                           self.tr("No polygon layer in actual project"),
                                           level=Qgis.Warning)
            return

        self.dlg.comboLidar.clear()
        # layers = QgsProject.instance().layerTreeRoot().children()
        layers = QgsProject.instance().mapLayers().values()
        layer_list = []
        n = 0
        for layer in layers:
            if layer.type() == QgsMapLayer.RasterLayer:
               layer_list.append(layer.name())
               self.dlg.comboLidar.addItems(layer_list)
               n += 1

        if n == 0:  # no Raster layer
            iface.messageBar().pushMessage(self.tr("Warning"),
                                           self.tr("No polygon layer in actual project"),
                                           level=Qgis.Warning)
            return

        # show the dialog
        self.dlg.show()

        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed

        if result:
            dirName = self.dlg.lineEdit.text().strip()
            if len(dirName) == 0:
                iface.messageBar().pushMessage(self.tr("Warning"),
                                               self.tr("Please select target folder"), level=Qgis.Warning)
                return
            if not (
            self.dlg.checkBox_6.isChecked(), self.dlg.checkPoint.isChecked(), self.dlg.checkVector.isChecked() or \
            self.dlg.checkBox.isChecked()):
                # self.dlg.checkRaster.isChecked()):
                iface.messageBar().pushMessage(self.tr("Warning"),
                                               self.tr(
                                                   "Neither vector nor raster layers selected for clipping. Nothing to do. "),
                                               level=Qgis.Warning)
                return

            index = self.dlg.comboBox.currentIndex()
            selection = self.dlg.comboBox.itemData(index)
            
            index = self.dlg.comboLidar.currentIndex()
            selection2 = self.dlg.comboLidar.itemData(index)
            
            checkedLayers = QgsProject.instance().layerTreeRoot().checkedLayers()

            # search existence of output folder, if not create it
            if not os.path.isdir(self.folderName):
                raise FileNotFoundError(
                    errno.ENOENT, os.strerror(errno.ENOENT), self.folderName)
            
            directory = self.folderName + "/Rasters"
            if not os.path.exists(directory) and self.dlg.checkBox_6.isChecked():
                os.makedirs(directory)
           
            directory = self.folderName + "/Vectors"
            if not os.path.exists(directory) and self.dlg.checkPoint.isChecked():
                os.makedirs(directory)
                
            directory = self.folderName + "/Vectors"
            if not os.path.exists(directory) and self.dlg.checkVector.isChecked():
                os.makedirs(directory)

            directory = self.folderName + "/CSV_Files"
            if not os.path.exists(directory) and self.dlg.checkBox.isChecked():
                os.makedirs(directory)

            # Progress bar
            progressMessageBar = iface.messageBar().createMessage(self.tr("Extracting & Analysing..."))
            progress = QProgressBar()
            progress.setMaximum(len(checkedLayers) - 1)
            progress.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
            progressMessageBar.layout().addWidget(progress)
            self.iface.messageBar().pushWidget(progressMessageBar, Qgis.Info)
            progression = 0

            # Analyses part
            for layer in checkedLayers:
                # clip raster layer (if displayed and checked)
                if layer.type() == QgsMapLayer.RasterLayer and layer != selection2 and self.dlg.checkBox_6.isChecked():
                    # get extension about the raster
                    filename, file_extension = os.path.splitext(layer.source())

                    output1 = self.folderName + "/Rasters/clip_" + layer.name() + file_extension

                    # check file isn't openned and is writable
                    version = 0
                    while self.isFileOpened(output1):
                        output1 = self.folderName + "/Rasters/clip_" + layer.name() + "(" + str(
                            version) + ")" + file_extension
                        version += 1

                    processing.run("gdal:cliprasterbymasklayer",
                                   {"INPUT": layer.id(), "MASK": selection.id(), "CROP_TO_CUTLINE": True,
                                    "OUTPUT": output1})
                   
                    # load layer
                    if self.dlg.checkBox_6.isChecked():
                        out = iface.addRasterLayer(output1, "")
                        if not out.isValid():
                            iface.messageBar().pushMessage(self.tr("Error"), self.tr("Could not load ") + output1,
                                                           level=Qgis.Warning)
                                       
                            # pixel resampling layer (if displayed and checked)
                if layer.type() == QgsMapLayer.RasterLayer and self.dlg.checkBox_6.isChecked():
                    
                    num_cell = self.dlg.spinSize.value()
                    
                    # # get extension about the raster
                    filename, file_extension = os.path.splitext(layer.source())
                    output2 = self.folderName + "/Rasters/rasSAGA_" + layer.name() + ".sdat"
                    output3 = self.folderName + "/Rasters/Resample_" + layer.name() + file_extension
                                                            
                    # check file isn't opened and is writable
                    version = 0
                    while self.isFileOpened(output2):
                        output2 = self.folderName + "/Rasters/rasSAGA_" + layer.name() + "(" + str(
                            version) + ").sdat"
                        version += 1
                        
                    # check file isn't opened and is writable
                    version = 0
                    while self.isFileOpened(output3):
                        output3 = self.folderName + "/Rasters/Resample_" + layer.name() + "(" + str(
                            version) + ")" + file_extension
                        version += 1
                                        
                    processing.run('saga:resampling',
                                   {"INPUT": output1, "KEEP_TYPE": True, "SCALE_UP": 0, "SCALE_DOWN": 0,
                                    "TARGET_USER_SIZE": num_cell, "TARGET_USER_FITS": 1, "OUTPUT": output2})
                                                               
                          # convert SAGA to GTiff (if displayed and checked)
                                        
                    processing.run("gdal:translate", {"INPUT": output2, "OUTPUT": output3})
                    
                    # load layer
                    if self.dlg.checkBox_6.isChecked():
                        out = iface.addRasterLayer(output3, "")
                        if not out.isValid():
                            iface.messageBar().pushMessage(self.tr("Error"), self.tr("Could not load ") + output3,
                                                           level=Qgis.Warning)
                        
            # Update progression
                time.sleep(1)
                progress.setValue(progression + 1)
                progression += 1

            self.iface.messageBar().clearWidgets()
            
            
            for layer in checkedLayers:                            
                            # pixel centroid conversion layer (if displayed and checked)
                if layer.type() == QgsMapLayer.RasterLayer and self.dlg.checkPoint.isChecked():
                                                                                                                                                                                                                                                                                                                                                                                                                   
                    # get extension about the raster
                    filename, file_extension = os.path.splitext(layer.source())
                    output4 = self.folderName + "/Vectors/innerPoints_" + layer.name() + ".shp"
                   
                    processing.run("qgis:pixelstopoints",
                                   {"INPUT_RASTER": output3, "RASTER_BAND": 1, "OUTPUT": output4})
          
            for layer in checkedLayers:
                            # polygons to lines (if displayed and checked)
                if layer.type() == QgsMapLayer.VectorLayer and self.dlg.checkVector.isChecked():
                                       
                    index = self.dlg.comboBox.currentIndex()
                    selection = self.dlg.comboBox.itemData(index)
                    layer = selection
                                
                    #filename, file_extension = os.path.splitext(layer.source())
                    output5 = self.folderName + "/Vectors/Lines_" + layer.name() + ".shp"
                    
                    # check file isn't opened and is writable
                    version = 0
                    while self.isFileOpened(output5):
                        output5 = self.folderName + "/Vectors/Lines_" + layer.name() + "(" + str(
                            version) + ").shp"
                        version += 1
                        
                    processing.run('native:polygonstolines', {"INPUT": layer, "OUTPUT": output5})
   
            for layer in checkedLayers:                
                            # generate points pixel centroids along line (if displayed and checked)
                if layer.type() == QgsMapLayer.RasterLayer and layer != selection2 and self.dlg.checkVector.isChecked():
                    
                    # # Get layer object
                    # fn = 'output5'
                    # vlayer = QgsVectorLayer(fn, '')
                    # ras_out = vlayer
                    
                    # index = self.dlg.comboLidar.currentIndex()
                    # selection = self.dlg.comboLidar.itemData(index)
                    # layer = selection
                    
                    filename, file_extension = os.path.splitext(layer.source())
                    output6 = self.folderName + "/Vectors/linePoints_" + layer.name() + ".shp"
                    
                    # check file isn't opened and is writable
                    version = 0
                    while self.isFileOpened(output6):
                        output6 = self.folderName + "/Vectors/linePoints_" + layer.name() + "(" + str(
                            version) + ").shp"
                        version += 1
                    
                    # processing.run("qgis:generatepointspixelcentroidsalongline", {"INPUT_RASTER": layer, "INPUT_VECTOR": ras_out, "OUTPUT": output6})
                    processing.run("qgis:generatepointspixelcentroidsalongline", {"INPUT_RASTER": layer, "INPUT_VECTOR": output5, "OUTPUT": output6})
            
            for layer in checkedLayers:
                           # union of line and point layers (if displayed and checked)
                if layer.type() == QgsMapLayer.VectorLayer and self.dlg.checkVector.isChecked():
                    
                    # # # Get layer object
                    # fn = 'output6'
                    # vlayer = QgsVectorLayer(fn, '')
                    # vec_out = vlayer
                    
                    # get extension about the vector
                    filename, file_extension = os.path.splitext(layer.source())
                    output7 = self.folderName + "/Vectors/joinPoints_" + layer.name() + ".shp"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
                                                             
                    # # check file isn't opened and is writable
                    version = 0
                    while self.isFileOpened(output7):
                        output7 = self.folderName + "/Vectors/joinPoints_" + layer.name() + "(" + str(
                            version) + ").shp"
                        version += 1
                    
                    processing.run('native:union', {"INPUT": output6, "OVERLAY": output4, "OUTPUT": output7})
                    
                    # load layer
                    if self.dlg.checkBox_6.isChecked():
                        out = iface.addVectorLayer(output7, "", "ogr")
                        if not out:
                            iface.messageBar().pushMessage(self.tr("Error"), self.tr("Could not load ") + output7,
                                                           level=Qgis.Warning)
         
            # Update progression
                time.sleep(1)
                progress.setValue(progression + 1)
                progression += 1

            self.iface.messageBar().clearWidgets()
            
            for layer in checkedLayers:
            
                            # # Export Lines to CSV File (if displayed and checked)
                if layer.type() == QgsMapLayer.VectorLayer and self.dlg.checkBox.isChecked():
                    # get extension about the vector
                    filename, file_extension = os.path.splitext(layer.source())
                    output8 = self.folderName + "/CSV_Files/xyLines_" + layer.name() + ".csv"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
                                                             
                    # # check file isn't opened and is writable
                    version = 0
                    while self.isFileOpened(output8):
                        output8 = self.folderName + "/CSV_Files/xyLines_" + layer.name() + "(" + str(
                            version) + ").csv"
                        version += 1
                    
                    processing.runAndLoadResults('native:addxyfields', {"INPUT": output6, "CRS": 'EPSG4326', "OUTPUT": output8})
       
            for layer in checkedLayers:
                            # # Export Points to CSV File (if displayed and checked)
                if layer.type() == QgsMapLayer.VectorLayer and self.dlg.checkBox.isChecked():
                    # get extension about the vector
                    filename, file_extension = os.path.splitext(layer.source())
                    output9 = self.folderName + "/CSV_Files/xyPoints_" + layer.name() + ".csv"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
                                                             
                    # # check file isn't opened and is writable
                    version = 0
                    while self.isFileOpened(output9):
                        output9 = self.folderName + "/CSV_Files/xyPoints_" + layer.name() + "(" + str(
                            version) + ").csv"
                        version += 1
                    
                    processing.runAndLoadResults('native:addxyfields', {"INPUT": output4, "CRS": 'EPSG4326', "OUTPUT": output9})
                    
                    # Execution Status Report
                    self.iface.messageBar().pushMessage("Success", "Output file written and loaded at " + output9, level=Qgis.Success, duration=3)
                    
                                        
            # Update progression
                time.sleep(1)
                progress.setValue(progression + 1)
                progression += 1

            self.iface.messageBar().clearWidgets()
            
