"""
 analysis_task

 wide area mapping
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                             -------------------
        begin                : 2022-01-01
        git sha              : $Format:%H$
        copyright            : (C) 2022 by forschung@ciss.de
        email                : forschung@ciss.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.core import QgsTask
from PyQt5.QtCore import *
from weissflaechenkartierung.src.xml.legend import Legend
from weissflaechenkartierung.src.xml.xml_config_ctrl import XMLConfigCtrl
from weissflaechenkartierung.src.xml.config import Config
from weissflaechenkartierung.src.qgis import QGisInterface
from weissflaechenkartierung.src.logger import Logger


class AnalysisResult:
    def __init__(self):
        self.result: bool = False
        self.result_txt: str = ""
        self.time_elapsed: float = 0.0
        self.time_started: float = 0.0
        self.layer_to_protocol: list = []


class AnalysisTaskLayerInfo:
    def __init__(self):
        self.layer_to_analyze = None
        self.blacklist: bool = False
        self.last_cfg: Config = None

    def __str__(self):
        return "layer: " + str(self.layer_to_analyze) + " | blacklist: " + str(self.blacklist) + " | cfg: " +\
               self.last_cfg.__str__()


class AnalysisTaskRunInfo:
    def __init__(self):
        self.description: str = "AnalyzeTask"
        self.run_with_errors: bool = False
        self.layer_list: list = []


class AnalysisTaskSignals(QObject):
    finished = pyqtSignal()               # signal to connect to
    result = pyqtSignal(AnalysisResult)           # signal to connect to


class AnalysisTask(QgsTask):
    def __init__(self, run_config: AnalysisTaskRunInfo):
        QgsTask.__init__(self, description=run_config.description, flags=QgsTask.CanCancel)
        self.signals = AnalysisTaskSignals()
        self._run_config: AnalysisTaskRunInfo = run_config
        self._analysis_result = AnalysisResult()

    def run(self) -> bool:
        """
        the action happens here
        """
        Logger.info("Start AnalysisTask", )

        try:
            r_list: list = []
            for layer_to_run in self._run_config.layer_list:
                analysed_layer = self._run(lay_info=layer_to_run)
                if analysed_layer is not layer_to_run.layer_to_analyze:
                    r_list.append(analysed_layer)
                else:
                    if not self._run_config.run_with_errors:
                        self._analysis_result.result_txt = "Analysis was not successfully"
                        Logger.data(self._analysis_result.result_txt)
                        return False
            # the analysis was successful
            self._analysis_result.layer_to_protocol = r_list
        except Exception as e:
            err_txt = "An irreparable error occurred while running analysis: " + str(e)
            self._analysis_result.result_txt = err_txt
            Logger.error(err_txt, exc_info=True)
            return False
        return True

    def _run(self, lay_info: AnalysisTaskLayerInfo):
        """
        runs the analysis
        """

        if lay_info.last_cfg is None:
            Logger.error("An irreparable error occurred while running analysis: The configuration is None", )
            return lay_info.layer_to_analyze

        Logger.debug("Analyze configuration: " + lay_info.last_cfg.name, )
        Logger.debug("Analyze layer name: " + lay_info.layer_to_analyze.name(), )
        if lay_info.layer_to_analyze.selectedFeatureCount() == 0:
            layer_to_analyze = QGisInterface.clone_layer(input_layer=lay_info.layer_to_analyze)
            Logger.debug("AnalyzeTask::run::there where no selections on the layer", )
        else:
            layer_to_analyze = QGisInterface.clone_selected_layer(input_layer=lay_info.layer_to_analyze)
            Logger.debug("AnalyzeTask::run::there where " + str(lay_info.layer_to_analyze.selectedFeatureCount()) + " selections on the layer", )

        self.setProgress(5)
        step_process = 1

        Logger.debug("Check last run config for layer", )
        if len(lay_info.last_cfg.module_list) == 0:
            Logger.info("There are no modules to run in analysis", )
        else:
            Logger.debug("There is/are: " + str(len(lay_info.last_cfg.module_list)) + " layer/s to run", )

            modules_to_rem = []
            for module in lay_info.last_cfg.module_list:

                try:
                    Logger.debug("Run module: " + module.name)
                    # run categorization (run result -> analyzed layer)
                    layer_to_analyze = module.run_categorization(cat_layer=layer_to_analyze).run_result

                    if layer_to_analyze is None:
                        Logger.error("The categorization was not successful", )
                        if self._run_config.run_with_errors:
                            continue
                        return layer_to_analyze
                except Exception:
                    if self._run_config.run_with_errors:
                        self._analysis_result.result_txt = Logger.get_current_system_exception()
                        modules_to_rem.append(module)
                        Logger.warning("Skipped module: " + module.name)
                        continue
                    else:
                        raise Exception(Logger.get_current_system_exception())

                self.setProgress(20 + (step_process * (20 / len(lay_info.last_cfg.module_list))))
                step_process += 1

            # check if there are modules to remove
            if len(modules_to_rem) > 0:
                Logger.warning("There where " + str(len(modules_to_rem)) + " failed module/s.")
                for m in modules_to_rem:
                    lay_info.last_cfg.module_list.remove(m)

            # create legend object
            Logger.debug("Get the names for the Legend")
            legend = Legend(xml_section=XMLConfigCtrl.get_first_tag_in_child_nodes(
                xml=lay_info.last_cfg.config_section,
                tag="legend"
            ),
                module=None)

            analyzed_layer = layer_to_analyze

            Logger.debug("Check if the layer is a black list layer")
            # check if the layer is a black list layer
            # set new layer names
            if lay_info.blacklist:
                legend.set_blacklisted_layer_name(analyzed_blacklisted_layer=analyzed_layer)
            else:
                legend.set_analyze_layer_name_param(analyzed_layer=analyzed_layer)
            return analyzed_layer
        return lay_info.layer_to_analyze

    def finished(self, result):
        """
        called when the task has finished
        """
        Logger.debug("Analysis task finished: " + str(result), )
        # set the run result
        self._analysis_result.result = result

        # emit the wfk analysis
        self.signals.result.emit(self._analysis_result)
        self.signals.finished.emit()

    def has_to_stop(self) -> bool:
        """
        checks if the task needs to stopped
        """
        return True if self.isCanceled() else False