# WhereGroup GmbH, Johannes Kröger, 2023-07
# License: GPLv3
# Example script to showcase specific things, not meant
# for production use or as best-practise example of all
# the things involved. Handle with care!

from qgis.core import (
    Qgis,
    QgsExpressionContextUtils,
    QgsMessageLog,
    QgsProcessing,
    QgsProcessingAlgorithm,
    QgsProcessingLayerPostProcessorInterface,
    QgsProcessingParameterDistance,
    QgsProcessingParameterFeatureSource,
    QgsProcessingParameterVectorDestination,
    QgsProcessingUtils,
)

from qgis.PyQt.QtCore import QDateTime

import processing


class LayerPostProcessor(QgsProcessingLayerPostProcessorInterface):
    """Renames, styles and groups the layer."""

    def __init__(self, name_template: str):
        """TODO Docstring"""
        super().__init__()
        self.name_template = name_template

    def postProcessLayer(self, layer, context, feedback):
        """TODO Docstring"""
        feedback.pushInfo(f"Post-processing for layer {layer.name()!r}:")

        number_of_features = layer.featureCount()

        layer_name = self.name_template.format(n=number_of_features)
        feedback.pushInfo(f"Renaming to {layer_name!r}")
        layer.setName(layer_name)

        feedback.pushInfo("Assigning style...")
        if number_of_features > 50:
            feedback.pushInfo("Assigning style... Red!")
            layer.loadNamedStyle("/tmp/red.qml")
        else:
            feedback.pushInfo("Assigning style... Blue!")
            layer.loadNamedStyle("/tmp/blue.qml")

        feedback.pushInfo("Moving into layer group...")
        root = context.project().layerTreeRoot()
        layer_tree_layer = root.findLayer(layer.id())
        clone = layer_tree_layer.clone()
        parent = layer_tree_layer.parent()
        group = root.findGroup("Abgezählte Puffer")
        if not group:
            group = root.insertGroup(0, "Abgezählte Puffer")
        group.insertChildNode(0, clone)
        parent.removeChildNode(layer_tree_layer)

        feedback.pushInfo(f"Post-processing for layer {layer.name()!r}: Done!")


class BufferRenameStyleGroup(QgsProcessingAlgorithm):
    """TODO Docstring"""

    def __init__(self, *args, **kwargs):
        """TODO Docstring"""
        super().__init__(*args, **kwargs)
        self.layers_to_group = []

    def initAlgorithm(self, config=None):
        """TODO Docstring"""
        self.addParameter(
            QgsProcessingParameterFeatureSource(
                name="VECTOR",
                description="Vector layer",
                types=[QgsProcessing.TypeVectorAnyGeometry],
            )
        )

        self.addParameter(
            QgsProcessingParameterDistance(
                name="INITIAL_BUFFER_DISTANCE",
                description="Initial buffer distance",
                parentParameterName="VECTOR",  # for units
                defaultValue=1,
            )
        )

        self.addParameter(
            QgsProcessingParameterVectorDestination(
                name="BUFFER1",
                description="Buffer #1",
            )
        )

        self.addParameter(
            QgsProcessingParameterVectorDestination(
                name="BUFFER2",
                description="Buffer #2",
            )
        )

        self.addParameter(
            QgsProcessingParameterVectorDestination(
                name="BUFFER3",
                description="Buffer #3",
            )
        )

    def processAlgorithm(self, parameters, context, feedback):
        """TODO Docstring"""
        native_buffer1 = processing.run(
            "native:buffer",
            {
                "INPUT": parameters["VECTOR"],
                "DISTANCE": parameters["INITIAL_BUFFER_DISTANCE"],
                "OUTPUT": parameters["BUFFER1"],  # for QGIS to load
            },
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

        if feedback.isCanceled():
            return {}

        native_buffer2 = processing.run(
            "native:buffer",
            {
                "INPUT": parameters["VECTOR"],
                "DISTANCE": parameters["INITIAL_BUFFER_DISTANCE"] * 2,
                "DISSOLVE": True,
                "SEPARATE_DISJOINT": True,  # available since QGIS 3.32
                "OUTPUT": parameters["BUFFER2"],  # for QGIS to load
            },
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

        if feedback.isCanceled():
            return {}

        native_buffer3 = processing.run(
            "native:buffer",
            {
                "INPUT": parameters["VECTOR"],
                "DISTANCE": parameters["INITIAL_BUFFER_DISTANCE"] * 3,
                "DISSOLVE": True,
                "SEPARATE_DISJOINT": True,  # available since QGIS 3.32
                "OUTPUT": parameters["BUFFER3"],  # for QGIS to load
            },
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

        if feedback.isCanceled():
            return {}

        # Note: We declare the scope for the post processors to be
        # "global" so that they do not vanish when QGIS cleans up
        # after finishing the QgsProcessingAlgorithm - before the
        # post-processing itself is performed.
        # Ref: https://gis.stackexchange.com/a/384996/51035
        global layer_post_processors
        layer_post_processors = []

        for i, layer in enumerate(
            (native_buffer2["OUTPUT"], native_buffer3["OUTPUT"]),
            start=2,
        ):
            layer_renamer = LayerPostProcessor(
                name_template=f"Puffer {i}: Aufgelöst in {{n}} Objekte"
            )
            # We must not add post-processing if layer is not meant to be loaded
            # ref: https://qgis.org/pyqgis/master/core/QgsProcessingContext.html#qgis.core.QgsProcessingContext.layerToLoadOnCompletionDetails
            if context.willLoadLayerOnCompletion(layer):
                layer_details = context.layerToLoadOnCompletionDetails(layer)
                layer_details.setPostProcessor(layer_renamer)
                layer_post_processors.append(layer_renamer)

        # for other algos to use
        return {
            "BUFFER1": native_buffer1["OUTPUT"],
            "BUFFER2": native_buffer2["OUTPUT"],
            "BUFFER3": native_buffer3["OUTPUT"],
        }

    def name(self):
        return "bufferrenamestylegroup"

    def displayName(self):
        return "Buffer, Rename, Style, Group"

    def group(self):
        return "WhereGroup Blog Examples"

    def groupId(self):
        return "wgblog"

    def shortHelpString(self):
        return """TODO"""

    @classmethod
    def createInstance(cls):
        return cls()
