# -*- coding: utf-8 -*-
"""
/***************************************************************************
 ChloeEcolandscapesDialog
                                 A QGIS plugin
 Ce plugin met en oeuvre les concepts d'Ecopaysages,  de grain bocager et de continuités écologiques
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                             -------------------
        begin                : 2023-07-17
        git sha              : $Format:%H$
        copyright            : (C) 2023 by INRAE
        email                : contact.chloe@inrae.fr
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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, datetime

from qgis.PyQt import uic
from qgis.PyQt import QtWidgets
from qgis.core import QgsMapLayerProxyModel
from qgis.PyQt.QtWidgets import QPushButton, QDialogButtonBox
from ..helpers.input_layer_file_widget import InputLayerFileWidget
from ..helpers.constants import ALGORITHM_NAME_GRAIN_BOCAGER
from ..helpers.custom_logger import CustomLogger
from ...helpers.helpers import get_console_command
from ..helpers.command_worker import BackgroundCommandExecutor

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


def s(path):
    return path.replace(os.sep, "/")


class ChloeGrainDialog(QtWidgets.QDialog, GRAIN_FORM_CLASS):
    def __init__(self, parent=None):
        """Constructor."""
        super(ChloeGrainDialog, self).__init__(parent)
        # 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)
        self.inputMNHClayerfile = InputLayerFileWidget()
        self.inputMNHClayerfile.setFilters(QgsMapLayerProxyModel.RasterLayer)
        self.groupBox_MNHC.layout().addWidget(self.inputMNHClayerfile, 0, 1)

        self.radioButton_rasterMNHC.toggled.connect(self.setRasterMNHC)
        self.setRasterMNHC()

        self.inputplantationslayerfile = InputLayerFileWidget()
        self.inputplantationslayerfile.setFilters(
            QgsMapLayerProxyModel.LineLayer | QgsMapLayerProxyModel.PolygonLayer
        )
        self.groupBox_plantations.layout().insertWidget(
            0, self.inputplantationslayerfile
        )
        self.inputplantationslayerfile.connectLayerChangedSlot(
            self.mFieldComboBox_hauteurVegetation.setLayer
        )

        self.inputarasementslayerfile = InputLayerFileWidget()
        self.inputarasementslayerfile.setFilters(QgsMapLayerProxyModel.PolygonLayer)
        self.groupBox_arasements.layout().insertWidget(0, self.inputarasementslayerfile)

        self.groupBox_enjeux.toggled.connect(self.setEnjeux)
        self.setEnjeux()

        self._logger = CustomLogger(self.textEdit_journal, self.progressBar)
        self.pushButton_interrupt.clicked.connect(self.interruptWork)

        self.pushButton_run = QPushButton("Run")
        self.button_box.addButton(self.pushButton_run, QDialogButtonBox.ActionRole)
        self.pushButton_run.clicked.connect(self.run_grain)

        self._command_executor = BackgroundCommandExecutor(self)
        self._command_executor.finished.connect(self._on_command_finished)

        # self.mMapLayerComboBox_Plantations.mMapLayer.setFilters(QgsMapLayerProxyModel.VectorLayer)
        # self.mMapLayerComboBox_Plantations.layerChanged.connect(self.mFieldComboBox_hauteurVegetation.setLayer)

    def setEnjeux(self):
        self.checkBox_grainMask.setEnabled(self.groupBox_enjeux.isChecked())
        self.checkBox_clusters.setEnabled(self.groupBox_enjeux.isChecked())
        self.checkBox_enjeux.setEnabled(self.groupBox_enjeux.isChecked())

    def setRasterMNHC(self):
        self.inputMNHClayerfile.setEnabled(self.radioButton_rasterMNHC.isChecked())
        self.mQgsFileWidget_dirMNHC.setEnabled(self.radioButton_dirMNHC.isChecked())

    def create_properties_file(self) -> str:
        # récupérer dossier courant
        prop_file = (
            s(self.mQgsFileWidget_resultDir.filePath())
            + "/chloe_grain_"
            + datetime.datetime.now().strftime("%d-%m-%Y_%H-%M-%S.%f")
            + ".properties"
        )

        # créer le fichier properties
        with open(prop_file, "w") as f:
            f.write("procedure=grain_bocager\n")

            # les variables de la boite de dialogue :
            # onglet (1)

            # onglet (2)

            f.write(
                "grain_bocager_cellsize=" + self.doubleSpinBox_pixelSize.text() + "\n"
            )
            f.write("grain_bocager_window_radius=" + self.spinBox_radius.text() + "\n")
            f.write(
                "thresholds={"
                + self.doubleSpinBox_seuilFonctionnel.text()
                + ";"
                + self.doubleSpinBox_seuilPotentiel.text()
                + ";"
                + self.doubleSpinBox_seuilOuvert.text()
                + "}\n"
            )

            # Dossier de sortie
            dirPath = s(self.mQgsFileWidget_resultDir.filePath())
            prefix = self.lineEdit_resultPrefix.text()
            filepath = dirPath + "/" + prefix + "_"
            f.write("output_folder=" + dirPath + "\n")
            f.write("output_prefix=" + prefix + "\n")

            extent = self.mExtentGroupBox.outputExtent()
            if (
                extent.xMinimum() != 0.0
                or extent.xMaximum() != 0.0
                or extent.yMinimum() != 0.0
                or extent.yMaximum() != 0.0
            ):
                f.write(
                    "envelope={"
                    + str(extent.xMinimum())
                    + ";"
                    + str(extent.xMaximum())
                    + ";"
                    + str(extent.yMinimum())
                    + ";"
                    + str(extent.yMaximum())
                    + "}\n"
                )

            # self.mExtentGroupBox.currentExtent()
            if not self.mQgsDoubleSpinBox_buffer.text().isspace():
                f.write(
                    "buffer_area=" + str(self.mQgsDoubleSpinBox_buffer.value()) + "\n"
                )

            if self.radioButton_rasterMNHC.isChecked():
                f.write("bocage=" + s(self.inputMNHClayerfile.currentFilePath()) + "\n")
            else:
                f.write("bocage=" + s(self.mQgsFileWidget_dirMNHC.filePath()) + "\n")

            # scénario (onglet 3)
            if self.groupBox_arasements.isChecked():
                f.write(
                    "wood_removal="
                    + s(self.inputarasementslayerfile.currentFilePath())
                    + "\n"
                )

            if self.groupBox_plantations.isChecked():
                f.write(
                    "wood_planting="
                    + s(self.inputplantationslayerfile.currentFilePath())
                    + "\n"
                )
                if self.radioButton_valeurHauteurVegetation.isChecked():
                    f.write(
                        "height_planting="
                        + self.doubleSpinBox_hauteurVegetation.text()
                        + "\n"
                    )
                elif self.radioButton_champHauteurVegetation.isChecked():
                    f.write(
                        "height_planting_attribute="
                        + self.mFieldComboBox_hauteurVegetation.currentField()
                        + "\n"
                    )

            # sorties (onglet 4)
            treatment = ""

            if (
                self.checkBox_hauteurBoisements.isChecked()
            ):  # recuperation_hauteur_boisement
                treatment = "wood_height_recovery"
                f.write("wood_height=" + filepath + "hauteur_boisement.tif" + "\n")

            if self.checkBox_typesBoisements.isChecked():  # detection_type_boisement
                treatment = "wood_type_detection"
                f.write("wood_type=" + filepath + "type_boisement.tif" + "\n")

            if (
                self.checkBox_distanceInfluence.isChecked()
            ):  # calcul_distance_influence_boisement
                treatment = "influence_distance_calculation"
                f.write(
                    "influence_distance=" + filepath + "distance_influence.tif" + "\n"
                )

            if self.checkBox_grain.isChecked():  # calcul_grain_bocager
                treatment = "grain_bocager_calculation"
                f.write(
                    "grain_bocager="
                    + filepath
                    + "grain_bocager_"
                    + self.doubleSpinBox_pixelSize.text()
                    + "m.tif"
                    + "\n"
                )
            if self.checkBox_grain4classes.isChecked():  # calcul_grain_bocager
                treatment = "grain_bocager_calculation"
                f.write(
                    "grain_bocager_4classes="
                    + filepath
                    + "grain_bocager_"
                    + self.doubleSpinBox_pixelSize.text()
                    + "m_4classes.tif"
                    + "\n"
                )

            if (
                self.checkBox_enjeux.isEnabled() and self.checkBox_grainMask.isChecked()
            ):  # clusterisation_fonctionnalite
                treatment = "functional_clustering"
                f.write(
                    "functional_grain_bocager="
                    + filepath
                    + "grain_bocager_fonctionnel.tif"
                    + "\n"
                )
            if (
                self.checkBox_enjeux.isEnabled() and self.checkBox_clusters.isChecked()
            ):  # clusterisation_fonctionnalite
                treatment = "functional_clustering"
                f.write(
                    "functional_grain_bocager_clustering="
                    + filepath
                    + "grain_bocager_cluster_"
                    + self.doubleSpinBox_pixelSize.text()
                    + "m.tif"
                    + "\n"
                )

            if (
                self.checkBox_enjeux.isEnabled() and self.checkBox_enjeux.isChecked()
            ):  # calcul_enjeux_globaux
                treatment = "global_issues_calculation"
                f.write(
                    "issues_cellsize=" + self.doubleSpinBox_pixelEnjeux.text() + "\n"
                )
                f.write(
                    "issues_window_radius=" + self.spinBox_radiusEnjeux.text() + "\n"
                )
                f.write(
                    "functional_grain_bocager_proportion="
                    + filepath
                    + "proportion_grain_bocager_fonc"
                    + self.spinBox_radiusEnjeux.text()
                    + "m.tif"
                    + "\n"
                )
                f.write(
                    "functional_grain_bocager_fragmentation="
                    + filepath
                    + "fragmentation_grain_bocager_fonc"
                    + self.spinBox_radiusEnjeux.text()
                    + "m.tif"
                    + "\n"
                )

            # self.checkBox_scenarioDiffs.isChecked()
            f.write("treatment=" + treatment + "\n")
            f.write("force=true\n")

        f.close()
        return prop_file

    def control_parameters(self) -> str | None:
        errors = ""
        if (
            self.radioButton_rasterMNHC.isChecked()
            and self.inputMNHClayerfile.currentText() == ""
        ) or (
            self.radioButton_dirMNHC.isChecked()
            and self.mQgsFileWidget_dirMNHC.filePath() == ""
        ):
            errors = errors + "No MNHC input<br/>"
        dir = self.mQgsFileWidget_resultDir.filePath()
        if dir is None or dir == "":
            errors = errors + "No output directory<br/>"
        if errors != "":
            return errors

    def run_grain(self) -> None:
        if self._command_executor.is_running():
            return

        self.textEdit_journal.clear()
        self._logger.setProgress(0)
        self.tabWidget.setCurrentIndex(4)
        self.tab_journal.show()

        errors = self.control_parameters()
        if errors:
            self._logger.reportError("Erreurs de saisie :")
            self._logger.reportError(errors.replace("<br/>", "\n"))
            return

        prop_file: str = self.create_properties_file()
        try:
            command: str = get_console_command(prop_file)
        except RuntimeError as error:
            self._logger.reportError(str(error))
            return

        if command is None:
            return

        self._logger.begin_execution(ALGORITHM_NAME_GRAIN_BOCAGER)
        if not self._command_executor.start(command, self._logger):
            return

        self.pushButton_interrupt.setEnabled(True)
        self.pushButton_run.setEnabled(False)

    def _on_command_finished(self, ok: bool) -> None:
        self.pushButton_interrupt.setEnabled(False)
        self.pushButton_run.setEnabled(True)
        self._logger.setProgress(0)
        if ok:
            self._logger.pushSuccess("Exécution terminée avec succès.")
        elif self._logger.isCanceled():
            self._logger.reportError("Exécution interrompue par l'utilisateur.")
        self._logger.set_is_canceled(False)

    def reject(self) -> None:
        self.tabWidget.setCurrentIndex(0)
        super().reject()

    def interruptWork(self) -> None:
        self._logger.set_is_canceled(True)
        self._command_executor.cancel()
        self.pushButton_interrupt.setEnabled(False)
