# SPDX-License-Identifier: GPL-2.0-or-later

# QGIS
from qgis import processing
from qgis.core import (
    Qgis,
    QgsProcessing,
    QgsProcessingAlgorithm,
    QgsProcessingException,
    QgsProcessingMultiStepFeedback,
    QgsProcessingParameterMultipleLayers,
    QgsProcessingParameterNumber,
    QgsProcessingParameterString,
    QgsProcessingParameterVectorLayer,
    QgsProject,
)
from qgis.PyQt.QtCore import QCoreApplication


class Slice_3D(QgsProcessingAlgorithm):
    def tr(self, string):
        """
        Returns a translatable string with the self.tr() function.
        """
        return QCoreApplication.translate("Processing", string)

    def initAlgorithm(self, config=None):
        self.addParameter(
            QgsProcessingParameterVectorLayer(
                "linestring",
                "Linestring",
                types=[QgsProcessing.SourceType.TypeVectorLine],
                defaultValue=None,
            )
        )
        self.addParameter(
            QgsProcessingParameterNumber(
                "z_min",
                "Z MIN",
                type=QgsProcessingParameterNumber.Type.Double,
                defaultValue=None,
            )
        )
        self.addParameter(
            QgsProcessingParameterNumber(
                "z_max",
                "Z MAX",
                type=QgsProcessingParameterNumber.Type.Double,
                defaultValue=None,
            )
        )
        self.addParameter(
            QgsProcessingParameterNumber(
                "buffer_size",
                "Buffer Size",
                type=QgsProcessingParameterNumber.Type.Double,
                defaultValue=None,
            )
        )

        self.addParameter(
            QgsProcessingParameterMultipleLayers(
                "vector_layers",
                "Vector layers",
                layerType=QgsProcessing.SourceType.TypeVectorPolygon,
                defaultValue=[],
            )
        )

        self.addParameter(
            QgsProcessingParameterString(
                "output_layer_group_name",
                "Output layer group name",
                multiLine=False,
                defaultValue="",
                optional=True,
            )
        )

    def processAlgorithm(self, parameters, context, model_feedback):
        # Use a multi-step feedback, so that individual child algorithm
        # progress reports are adjusted for the
        # overall progress through the model
        feedback = QgsProcessingMultiStepFeedback(5, model_feedback)
        results = {}
        outputs = {}

        z_min = self.parameterAsDouble(parameters, "z_min", context)
        z_max = self.parameterAsDouble(parameters, "z_max", context)
        if z_min >= z_max:
            raise QgsProcessingException(
                self.tr("Z maximum needs to be greater than Z minimum")
            )

        # Buffer
        alg_params = {
            "DISSOLVE": False,
            "DISTANCE": parameters["buffer_size"],
            "END_CAP_STYLE": 0,  # Round
            "INPUT": parameters["linestring"],
            "JOIN_STYLE": 0,  # Round
            "MITER_LIMIT": 2,
            "SEGMENTS": 5,
            "SEPARATE_DISJOINT": False,
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["Buffer"] = processing.run(
            "native:buffer",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

        feedback.setCurrentStep(1)
        if feedback.isCanceled():
            return {}

        # Multipart to singleparts
        alg_params = {
            "INPUT": outputs["Buffer"]["OUTPUT"],
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["MultipartToSingleparts"] = processing.run(
            "native:multiparttosingleparts",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

        feedback.setCurrentStep(2)
        if feedback.isCanceled():
            return {}

        # Translate_3D
        alg_params = {
            "INPUT": outputs["MultipartToSingleparts"]["OUTPUT"],
            "TRANSLATE_X": 0,
            "TRANSLATE_Y": 0,
            "TRANSLATE_Z": parameters["z_min"],
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["Translate_3d"] = processing.run(
            "sfcgal:Translate_3D",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

        results["Translate_3d"] = outputs["Translate_3d"]["OUTPUT"]

        feedback.setCurrentStep(3)
        if feedback.isCanceled():
            return {}

        # Extrude
        z_extrude = parameters["z_max"] - parameters["z_min"]
        alg_params = {
            "EXTRUDE_X": 0,
            "EXTRUDE_Y": 0,
            "EXTRUDE_Z": z_extrude,
            "FORCE_SINGLEPART": True,
            "INPUT": outputs["Translate_3d"]["OUTPUT"],
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["Extrude"] = processing.run(
            "sfcgal:Extrude",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

        feedback.setCurrentStep(4)
        if feedback.isCanceled():
            return {}

        # Group handling
        root = QgsProject.instance().layerTreeRoot()
        group_name_base = "cut"
        group_name = group_name_base
        counter = 1
        while root.findGroup(group_name):
            group_name = f"{group_name_base}{counter}"
            counter += 1
        new_group = root.insertGroup(0, group_name)

        # Intersection 3D
        for vector_layer in self.parameterAsLayerList(
            parameters, "vector_layers", context
        ):
            alg_params = {
                "FORCE_SOLID": True,
                "INPUT": vector_layer,
                "OVERLAY": outputs["Extrude"]["OUTPUT"],
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            output_intersection = processing.run(
                "sfcgal:Intersection_3D",
                alg_params,
                context=context,
                feedback=feedback,
                # Si ça a FALSE, on aurait direct le layer dans OUTPUT (a tester)
                is_child_algorithm=True,
            )

            if not output_intersection:
                raise QgsProcessingException(
                    "The result of the slice is null, as no entity in the "
                    "source layer intersects an entity in the overlay layer."
                )
            intersection_output_id = output_intersection["OUTPUT"]
            intersection_layer = context.getMapLayer(intersection_output_id)
            intersection_layer.setName(f"{vector_layer.name()}_cut")

            if not intersection_layer.isValid():
                raise QgsProcessingException(
                    f"Unable to load intersection layer for {intersection_output_id}"
                )
            # Add layer to canvas
            QgsProject.instance().addMapLayer(intersection_layer, False)
            new_group.addLayer(intersection_layer)

            # Style
            style_name_source = vector_layer.styleManager().currentStyle()
            style_source = vector_layer.styleManager().style(style_name_source)
            intersection_layer.styleManager().addStyle("mystyle", style_source)
            intersection_layer.styleManager().setCurrentStyle("mystyle")
            intersection_layer.triggerRepaint()

            vector_layer_uri = vector_layer.dataProvider().dataSourceUri()
            results[vector_layer_uri] = intersection_layer

            if feedback.isCanceled():
                return {}

        return results

    def name(self):
        return "Slice_3D"

    def displayName(self):
        return self.tr("Slice 3D")

    def group(self):
        """
        Returns the name of the group this algorithm belongs to. This string
        should be localised.
        """
        return self.tr("3D")

    def groupId(self):
        """
        Returns the unique ID of the group this algorithm belongs to. This
        string should be fixed for the algorithm, and must not be localised.
        The group id should be unique within each provider. Group id should
        contain lowercase alphanumeric characters only and no spaces or other
        formatting characters.
        """
        return "3d"

    def createInstance(self):
        return Slice_3D()

    def flags(self):
        try:
            flag = Qgis.ProcessingAlgorithmFlag.NoThreading
        except AttributeError:
            flag = QgsProcessingAlgorithm.ProcessingAlgorithmFlag.FlagNoThreading
        return super().flags() | flag
