# -*- coding: utf-8 -*-
"""
/***************************************************************************
 Module
                                 A QGIS plugin
 wide are mapping
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                             -------------------
        begin                : 2020-11-13
        git sha              : $Format:%H$
        copyright            : (C) 2020 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 weissflaechenkartierung.src.xml.result import RunResult
# we need all class imports for eval!
from weissflaechenkartierung.src.xml.connector import Connector, LocalFileConnector, RemoteFileConnector
from weissflaechenkartierung.src.xml.layer import LayerMixIn, EegCsvLayer, ShapeLayer
from weissflaechenkartierung.src.xml.categorization import Categorization, \
    ByPositionCategorization, ByPositionCategorizationWithClipping, AdditionalInformationCategorization, \
    ByCentroidCategorization, ByDistanceCategorization, USUDistanceCategorization, SPARQLCategorization
from weissflaechenkartierung.src.xml.difference import Difference, BlacklistingDifference, BufferDifference, \
    SelectiveBufferDifference

from weissflaechenkartierung.src.logger import Logger
import weissflaechenkartierung.src.xml.xml_config_ctrl as xml
import weissflaechenkartierung.src.xml.legend as leg


import time


class Module:
    """
    class which holds all information about a module

    - can run layer
    - can run connector
    - can run differences
    - can run categorizations
    - can add "legends" to the map
    """

    def __init__(self, xml_section, config, name: str, parameters: dict, active: bool = True):
        """
        :param xml_section: selection of an xml
        :param config: config class
        :param name: name of the module
        :param parameters: parameters of the module
        :param active: active state of the module
        """
        self._xml_section = xml_section
        self._config = config
        self._name = name
        self._parameters = parameters
        self._active = active

        self._analyze_layer = None
        self._wfk_layer = None
        self._connector = None
        self._layer = None
        self._difference = None
        self._categorization = None
        self._legend = None

        # result
        self._run_result = RunResult()

    def __str__(self):
        return "name: " + self._name + "\nactive: " + str(self._active) + "\nparameters: " + str(self._parameters) \
               + "\nrun result: " + str(self._run_result.run_result) + "\nelapsed time: " \
               + str(self._run_result.get_elapsed_time()) + "\nconfig name: " + self._config.name

    @property
    def analyze_layer(self):
        """
        returns the analyze_layer of the module
        """
        return self._analyze_layer

    @analyze_layer.setter
    def analyze_layer(self, value):
        """
        sets the analyze_layer layer of the module
        """
        self._analyze_layer = value

    @property
    def wfk_layer(self):
        """
        returns the wfk layer of the module
        """
        return self._wfk_layer

    @wfk_layer.setter
    def wfk_layer(self, value):
        """
        sets the wfk layer of the module
        """
        self._wfk_layer = value

    @property
    def layer(self):
        """
        returns the layer of the module
        """
        return self._layer

    @property
    def connector(self):
        """
        returns the module connector
        """
        return self._connector

    @property
    def difference(self):
        """
        returns the difference object
        """
        return self._difference

    @property
    def categorization(self):
        """
        returns the categorization object
        """
        return self._categorization

    @property
    def legend(self):
        """
        returns the legend object
        """
        return self._legend

    @property
    def active_state(self) -> bool:
        """
        returns the active state of the module
        """
        return self._active

    @property
    def parameters(self) -> dict:
        """
        returns the parameters of the module
        """
        return self._parameters

    @property
    def name(self) -> str:
        """
        returns the name of the module
        """
        return self._name

    @property
    def config(self):
        """
        returns the config of the module
        """
        return self._config

    @property
    def xml_selection(self):
        """
        returns the xml selection of the module
        """
        return self._xml_section

    # ####################################################

    def run_connector(self) -> RunResult:
        """
        run the connector

        :return RunResult
        """
        run_result = RunResult()
        Logger.debug("Run connector", )

        if not self._run_connector():
            run_result.run_result = False

        run_result.end_time = time.time()
        return run_result

    def _run_connector(self) -> bool:
        """
        """
        if not self._active:
            return False
        connector_tag = xml.XMLConfigCtrl.get_first_tag_in_child_nodes(xml=self._xml_section, tag="connector")
        if connector_tag is not None:
            c_type: str = xml.XMLConfigCtrl.get_type_from_xml(element=connector_tag)
            if c_type is not None and c_type != "":
                self._connector = eval(c_type)(connector_tag, self)
        else:
            self._connector = Connector(xml_section=connector_tag, module=self)
        self._connector.retrieve_data()
        return True

    # ####################################################

    def run_layer(self):
        """
        runs the layer
        """
        run_result = RunResult()
        Logger.debug("Run layer", )

        if not self._run_layer():
            run_result.run_result = False

        run_result.end_time = time.time()
        return run_result

    def _run_layer(self) -> bool:
        """
        """
        if not self._active:
            return False

        layer_tag = xml.XMLConfigCtrl.get_first_tag_in_child_nodes(xml=self._xml_section, tag="layer")
        if layer_tag is not None:
            c_type = xml.XMLConfigCtrl.get_type_from_xml(element=layer_tag)
            if c_type is not None and c_type != "":
                self._layer = eval(c_type)(layer_tag, self)
        else:
            self._layer = LayerMixIn(xml_section=layer_tag, module=self)
        return self._layer.create_layer()

    # ####################################################

    def run_difference(self, diff_layer):
        """
        runs the diff
        """
        Logger.debug("Run difference", )
        run_result = RunResult()
        run_result.run_result = self._run_difference(diff_layer=diff_layer)
        run_result.end_time = time.time()
        return run_result

    def _run_difference(self, diff_layer):
        if not self._active:
            return diff_layer

        diff_tag = xml.XMLConfigCtrl.get_first_tag_in_child_nodes(xml=self._xml_section, tag="difference")
        if diff_tag is not None:
            c_type = xml.XMLConfigCtrl.get_type_from_xml(element=diff_tag)
            if c_type is not None and c_type != "":
                self._difference = eval(c_type)(diff_tag, self)
        else:
            self._difference = Difference(diff_tag, self)
        return self._difference.run(diff_layer)

    # ####################################################

    def run_categorization(self, cat_layer) -> RunResult:
        """
        runs the categorization of the module
        return: RunResult
        """
        Logger.debug("Run categorization")
        run_result = RunResult()
        run_result.run_result = self._run_categorization(cat_layer=cat_layer)
        run_result.end_time = time.time()
        return run_result

    def _run_categorization(self, cat_layer):
        if not self._active:
            return cat_layer

        cat_tags: list = xml.XMLConfigCtrl.get_all_tags_in_child_nodes(xml=self._xml_section, tag="categorization")

        result_layer = cat_layer
        for cat_tag in cat_tags:
            if cat_tag is not None:
                c_type = xml.XMLConfigCtrl.get_type_from_xml(element=cat_tag)
                if c_type is not None and c_type != "":
                    self._categorization = eval(c_type)(cat_tag, self)
            else:
                self._categorization = Categorization(cat_tag, self)
            result_layer = self.categorization.run(result_layer)
        return result_layer

    # ####################################################

    def add_to_map(self):
        if not self._active:
            return []
        Logger.debug("Add_to_map", )
        l = leg.Legend(xml_section=xml.XMLConfigCtrl.get_first_tag_in_child_nodes(
            xml=self._xml_section,
            tag="legend"
        ),
            module=self)
        return l.add_mod_layer_to_map()
