# -*- coding: utf-8 -*-
"""
/***************************************************************************
 UrbanSARCA
                                 A QGIS plugin
 Plugin for analysis of radioactive contamination ov vegetation cover in urban environment 
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2020-03-31
        git sha              : $Format:%H$
        copyright            : (C) 2020-2022 by Jakub Brom, ENKI o.p.s. Třeboň
        email                : jbrom@lanres.cz
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
"""
from qgis.PyQt.QtCore import QSettings, QTranslator, \
    QCoreApplication, Qt, QUrl
from qgis.PyQt.QtGui import QIcon, QDesktopServices, QImage, QPixmap
from qgis.PyQt.QtWidgets import QAction, QFileDialog, QComboBox, \
    QPushButton, QMessageBox
# Initialize Qt resources from file resources.py
from .resources import *

from qgis.core import Qgis, QgsMapLayerProxyModel

# Import the code for the DockWidget
from .urban_sarca_dockwidget import UrbanSARCADockWidget
import sys
import urllib.request

from .urban_sarca_lib import *
from .overlap_clip import *
from .SEBCS_lib import GeoIO, VegIndices
geo = GeoIO()
vi = VegIndices()


class UrbanSARCA:
    """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
        self.locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            'UrbanSARCA_{}.qm'.format(self.locale))

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

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

        # print "** INITIALIZING UrbanSARCA"

        self.pluginIsActive = False
        self.dockwidget = None

        # Initial output folder path
        self.out_folder_path = os.path.expanduser("~")

    # 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('Urban Green SARCA', 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)

        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/urban_sarca/icon.png'
        self.add_action(
            icon_path,
            text=self.tr(u'Urban Green SARCA'),
            callback=self.run,
            parent=self.iface.mainWindow())

        if not self.pluginIsActive:
            self.pluginIsActive = True

            # dockwidget may not exist if:
            #    first run of plugin
            #    removed on close (see self.onClosePlugin method)
            if self.dockwidget == None:
                # Create the dockwidget (after translation) and keep reference
                self.dockwidget = UrbanSARCADockWidget()

        # Help
        self.dockwidget.buttonBox.helpRequested.connect(self.pluginHelp)

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

    def setCboxEmpty(self, comboBox):
        """Setting of empty value (text) in comboBoxes"""

        comboBox.setAdditionalItems([""])
        ind = comboBox.count() - 1
        comboBox.setCurrentIndex(ind)

    def pluginHelp(self):
        """Open the help file.
        """
        help_file = os.path.join(self.plugin_dir, "help", "build",
                                 "html", "index.html")
        help_file_norm = os.path.normpath(help_file)

        try:
            if sys.platform != "win32":
                QDesktopServices.openUrl(QUrl(help_file_norm,
                                              QUrl.TolerantMode))
            else:
                print(help_file)
                print(help_file_norm)
                os.startfile(help_file_norm)

        except IOError:
            self.iface.messageBar().pushMessage(self.tr("Help error"),
                    self.tr("Ooops, an error occured during help file"
                    " opening..."), level=Qgis.Warning, duration=5)

    def selectFile(self, comboBox):
        """
        Opening the raster layer file and adding the path to the combobox
        on end of the list with comboBox items.

        :param comboBox: Qt combobox.
        :type comboBox: QComboBox
        """

        try:
            rast_path = QFileDialog.getOpenFileName(None,
                                                    self.tr(
                                                        "Select file"),
                                                    self.out_folder_path,
                                                    "Images (*.*);; "
                                                    "GeoTIFF (*.tif);; "
                                                    "Idrisi Raster (*.rst);; "
                                                    "Erdas Imagine (*.img);; "
                                                    "GDAL Virtual (*.vrt);; "
                                                    "ENVI (*.hdr)")

            comboBox.setAdditionalItems([rast_path[0]])
            ind = comboBox.count() - 1
            comboBox.setCurrentIndex(ind)

        except FileNotFoundError:
            self.iface.messageBar().pushMessage(self.tr("Path error"),
                                                self.tr(
                                                    "Selected file has not "
                                                    "been found."),
                                                level=Qgis.Warning)

    def outFolder(self):
        """
        Set output folder path.
        """
        self.dockwidget.le_out_folder.clear()

        try:
            self.out_folder_path = QFileDialog.getExistingDirectory(
                None,
                self.tr("Select output folder"),
                self.out_folder_path)
            self.dockwidget.le_out_folder.setText(
                str(self.out_folder_path))
        except Exception:
            self.iface.messageBar().pushMessage(
                self.tr("Path error"),
                self.tr(
                    "Output folder path has not been selected"),
                level=Qgis.Warning)

    def onClosePlugin(self):
        """Cleanup necessary items here when plugin dockwidget is closed"""

        #print "** CLOSING UrbanSARCA"

        # disconnects
        self.dockwidget.closingPlugin.disconnect(self.onClosePlugin)

        # remove this statement if dockwidget is to remain
        # for reuse if plugin is reopened
        # Commented next statement since it causes QGIS crashe
        # when closing the docked window:
        # self.dockwidget = None

        self.dockwidget.le_out_folder.clear()
        self.dockwidget.sbox_precip.setValue(0.00)
        self.dockwidget.sbox_ru1.setValue(5000)
        self.dockwidget.sbox_ru2.setValue(3000000)
        self.dockwidget.sbox_hl.setValue(1000)
        self.dockwidget.cbox_cont.setCurrentIndex(0)
        self.dockwidget.rb_precip_konst.setChecked(True)

        self.pluginIsActive = False

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""

        #print "** UNLOAD UrbanSARCA"

        for action in self.actions:
            self.iface.removePluginMenu(
                self.tr(u'&UrbanSARCA'),
                action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar

    def calculate(self):
        """Processing of calculation"""

        # Import layers and data
        # Rasters
        try:
            red_index = self.dockwidget.cbox_red.currentIndex()
            red_path = self.dockwidget.cbox_red.layer(red_index).source()
        except Exception:
            red_path = self.dockwidget.cbox_red.currentText()

        if red_path is None or red_path == "":
            self.iface.messageBar().pushMessage(
                        self.tr("Path error"),
                        self.tr("Path to RED band has not been selected"),
                                level=Qgis.Warning)
            sys.tracebacklimit = None
            raise FileNotFoundError(self.tr("Path to RED band has not been selected"))

        # TODO: vyresit vyjimky - prekryv vypisu vyjimky...

        try:
            nir_index = self.dockwidget.cbox_nir.currentIndex()
            nir_path = self.dockwidget.cbox_nir.layer(nir_index).source()
        except Exception:
            nir_path = self.dockwidget.cbox_nir.currentText()

        if nir_path is None or nir_path == "":
            self.iface.messageBar().pushMessage(
                        self.tr("Path error"),
                        self.tr("Path to NIR band has not been selected"),
                                level=Qgis.Warning)
            sys.tracebacklimit = None
            raise FileNotFoundError(self.tr("Path to NIR band has not been "
                                            "selected"))

        try:
            depo_index = self.dockwidget.cbox_depo.currentIndex()
            depo_path = self.dockwidget.cbox_depo.layer(depo_index).source()
        except Exception:
            depo_path = self.dockwidget.cbox_depo.currentText()

        if depo_path is None or depo_path == "":
            self.iface.messageBar().pushMessage(
                        self.tr("Path error"),
                        self.tr("Path to radioactive contamination layer has "
                                "not been selected"),
                                level=Qgis.Warning)
            sys.tracebacklimit = None
            raise FileNotFoundError(self.tr("Path to radioactive "
                                            "contamination layer "
                                            "has not been selected"))

        # Precipitation raster
        if self.dockwidget.rb_precip_konst.isChecked() == True:
            precip = self.dockwidget.sbox_precip.value()
            precip_path = None
        else:
            try:
                precip_index = self.dockwidget.cbox_precip.currentIndex()
                precip_path = self.dockwidget.cbox_precip.layer(
                    precip_index).source()
            except Exception:
                precip_path = self.dockwidget.cbox_precip.currentText()

            if precip_path is None or precip_path == "":
                self.iface.messageBar().pushMessage(
                    self.tr("Path error"),
                    self.tr("Path to precipitation layer has not been "
                        "selected"),
                    level=Qgis.Warning)
                sys.tracebacklimit = None
                raise FileNotFoundError(self.tr("Path to precipitation layer "
                                                "has not been selected"))

        # Radionuclide
        radio_index = self.dockwidget.cbox_cont.currentIndex()
        if radio_index == 0:
            radionuclide = 1.0
        elif radio_index == 1 or radio_index == 3:
            radionuclide = 2.0
        elif radio_index == 2:
            radionuclide = 0.5
        else:
            radionuclide = 1.0

        # Constants
        ref_level1 = self.dockwidget.sbox_ru1.value()
        ref_level2 = self.dockwidget.sbox_ru2.value()
        hyg_lim = self.dockwidget.sbox_hl.value()

        # Output path
        self.out_folder = self.dockwidget.le_out_folder.text()
        # If outpath is empty, the home path is selected
        if self.out_folder is None or self.out_folder == "":
            self.out_folder = os.path.expanduser("~")

        # Calculation
        # TODO: zkusit vyresit mazani docasnych souboru tady
        if precip_path != None:
            # 1. Layers clipping
            in_lyrs = [red_path, nir_path, depo_path, precip_path]
            (cl_red, cl_nir, cl_depo, cl_precip) = clipOverlappingArea(
                in_lyrs, tmp_out=True)

            # 2. Layers import to Numpy arrays
            in_red = geo.rasterToArray(cl_red)
            in_nir = geo.rasterToArray(cl_nir)
            in_depo = geo.rasterToArray(cl_depo)
            precip = geo.rasterToArray(cl_precip)
        else:
            # 1. Layers clipping
            in_lyrs = [red_path, nir_path, depo_path]
            (cl_red, cl_nir, cl_depo) = clipOverlappingArea(in_lyrs,
                                                               tmp_out=True)

            # 2. Layers import to Numpy arrays
            in_red = geo.rasterToArray(cl_red)
            in_nir = geo.rasterToArray(cl_nir)
            in_depo = geo.rasterToArray(cl_depo)
            in_depo = np.nan_to_num(in_depo)

        # Reading geographic information on layers
        (gtransf, prj, x_size, y_size, EPSG) = geo.readGeo(cl_red)

        # 2. Veg. indices
        ndvi = vi.viNDVI(in_red, in_nir)
        msavi = vi.viMSAVI(in_red, in_nir)
        lai = vi.LAI(in_red, in_nir, self.lai_method)
        biom = vi.biomass_sat(ndvi)

        # 3. IF and radioactive deposition calculation
        IF = interceptFactor(lai, precip, biom, radionuclide)
        dep_biom = contBiomass(in_depo, IF)
        dep_soil = contSoil(in_depo, IF)
        cont_weight = contMass(dep_biom, biom)

        # 4. RU and hyg_lim masks
        mask_HL = hygLimit(cont_weight, hyg_lim)
        mask_RU = referLevel(in_depo, ref_level1, ref_level2)

        # 5. Layers export
        output = {
            "Biomass": biom,
            "Intercept_factor": IF,
            "Veg_contamination": dep_biom,
            "Soil_contamination": dep_soil,
            "Mass_contamination": cont_weight,
            "Mask_hyg_lim": mask_HL,
            "Mask_ref_levels": mask_RU
        }

        # "LAI": lai,
        # "NDVI": ndvi,
        # "MSAVI": msavi

        self.outlyrs = list(output.values())
        self.outnames = list(output.keys())
        geo.arrayToRast(self.outlyrs, self.outnames, prj, gtransf, EPSG,
                        self.out_folder)

        # Import results to QGIS legend/canvas
        self.iface.messageBar().clearWidgets()
        widget = self.iface.messageBar().createMessage("Info", self.tr(
            "Calculation has been done. Do you want to load "
            "calculated layers in to QGIS?"))
        button = QPushButton(widget)
        button.setText(self.tr("Load layers"))
        button.pressed.connect(self.loadRasters)
        widget.layout().addWidget(button)
        self.iface.messageBar().pushWidget(widget, Qgis.Info)

    def loadRasters(self):
        """Uploading of the results (calculated layers) in to the
        QGIS legend.
        """

        out_suffix = ".tif"

        # List of output files paths
        for i in range(0, len(self.outlyrs)):
            try:
                out_file = os.path.join(self.out_folder,
                            self.outnames[i] + out_suffix)  # construction of the path to the resulted layer
                self.iface.addRasterLayer(out_file, self.outnames[i])  # opening the resulted layer
            except Exception:
                self.iface.messageBar().pushMessage("Info",
                    self.tr("File " + self.outnames[i] +
                    " has not been uploaded in to QGIS."),
                    level=Qgis.Warning)
        self.iface.messageBar().clearWidgets()

    def showInfo(self):

        # msgbox text
        if self.locale == "cs":
            info_text = "Vývoj programu Urban Green SARCA byl " \
                        "finančně podpořen projektem Ministerstva " \
                        "vnitra České republiky VH20172020015"
        else:
            info_text = self.tr("Urban Green SARCA plug-in "
                                "development has been supported by "
                                "project of Ministry of the Interior "
                                "of the Czech Republic No. "
                                "VH20172020015")

        # set path to msgbox icon

        # icon_path = 'https://upload.wikimedia.org/wikipedia/commons/7/79/MV_%C4%8CR.png'
        # data = urllib.request.urlopen(icon_path).read()

        localdir = os.path.dirname(__file__)
        data = os.path.join(localdir, "MV_icon.png")

        # Create icon
        image = QImage()
        image.load(data)

        pixmap = QPixmap(image).scaledToHeight(128,
                                            Qt.SmoothTransformation)

        # MsgBox
        msg_box = QMessageBox()
        msg_box.setIconPixmap(pixmap)
        msg_box.setText(info_text)
        msg_box.setWindowTitle(self.tr("Acknowledgement"))
        msg_box.exec()

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

    def run(self):
        """Run method that loads and starts the plugin"""

        # print "** STARTING UrbanSARCA"
        self.showInfo()

        # Set path to out folder
        self.dockwidget.pb_out.clicked.connect(self.outFolder)

        # Initial setting of the empty cboxes
        self.setCboxEmpty(self.dockwidget.cbox_red)
        self.setCboxEmpty(self.dockwidget.cbox_nir)
        self.setCboxEmpty(self.dockwidget.cbox_depo)
        self.setCboxEmpty(self.dockwidget.cbox_precip)

        # If precipitation is constant the cbox for precipitation
        # raster is empty
        self.dockwidget.rb_precip_konst.toggled.connect(lambda:
                self.setCboxEmpty(self.dockwidget.cbox_precip))
        self.dockwidget.rb_precip_rast.toggled.connect(lambda:
            self.dockwidget.cbox_precip.setCurrentIndex(0))
        # If raster of precipitation is selected, value in spinbox is set
        # to 0.0
        self.dockwidget.rb_precip_rast.toggled.connect(lambda:
            self.dockwidget.sbox_precip.setValue(0.00))

        # Read list of names and layers paths from QGIS legend
        self.dockwidget.cbox_red.setFilters(QgsMapLayerProxyModel.RasterLayer)
        self.dockwidget.cbox_nir.setFilters(QgsMapLayerProxyModel.RasterLayer)
        self.dockwidget.cbox_depo.setFilters(QgsMapLayerProxyModel.RasterLayer)
        self.dockwidget.cbox_precip.setFilters(
            QgsMapLayerProxyModel.RasterLayer)

        # Set path to selected raster files to CBoxes
        self.dockwidget.pb_red.clicked.connect(lambda:
            self.selectFile(self.dockwidget.cbox_red))
        self.dockwidget.pb_nir.clicked.connect(
            lambda: self.selectFile(self.dockwidget.cbox_nir))
        self.dockwidget.pb_depo.clicked.connect(
            lambda: self.selectFile(self.dockwidget.cbox_depo))
        self.dockwidget.pb_precip.clicked.connect(
            lambda: self.selectFile(self.dockwidget.cbox_precip))

        # Set LAI method
        self.lai_method = self.dockwidget.cbox_lai.currentIndex()

        # Calculate
        self.dockwidget.buttonBox.accepted.connect(self.calculate)

        # connect to provide cleanup on closing of dockwidget
        self.dockwidget.closingPlugin.connect(self.onClosePlugin)

        # show the dockwidget
        # TODO: fix to allow choice of dock location
        self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dockwidget)
        self.dockwidget.show()
