from qgis.core import (
    QgsProcessing,
    QgsProcessingParameterVectorLayer,
    QgsProcessingParameterNumber,
    QgsFeatureRequest,
    QgsProcessingParameterFeatureSource,
    QgsProcessingParameterMultipleLayers,
    QgsProcessingParameterBoolean,
    QgsProcessingParameterField,
    QgsProcessingMultiStepFeedback,
    QgsProcessingException,
)
from qgis.utils import iface

from PreCourlis.processing.precourlis_algorithm import PreCourlisAlgorithm

import processing


class AddIntermediateProfilesAlgorithm(PreCourlisAlgorithm):
    INPUT = "INPUT"
    AXIS = "AXIS"
    CONSTRAINT_LINES = "CONSTRAINT_LINES"
    CREATE_CONSTRAINT_LINES_ENDS_AT_END_OF_PROFILES = (
        "CREATE_CONSTRAINT_LINES_ENDS_AT_END_OF_PROFILES"
    )
    LONG_STEP = "LONG_STEP"
    LAT_STEP = "LAT_STEP"
    ATTR_CROSS_SECTION = "ATTR_CROSS_SECTION"

    def initAlgorithm(self, config=None):

        self.addParameter(
            QgsProcessingParameterVectorLayer(
                self.INPUT,
                self.tr("Input"),
                types=[QgsProcessing.TypeVectorLine],
                defaultValue=None,
            )
        )
        self.addParameter(
            QgsProcessingParameterFeatureSource(
                self.AXIS,
                self.tr("Axis"),
                types=[QgsProcessing.TypeVectorLine],
                defaultValue=None,
            )
        )
        self.addParameter(
            QgsProcessingParameterMultipleLayers(
                self.CONSTRAINT_LINES,
                self.tr("Contraint lines"),
                layerType=QgsProcessing.TypeVectorLine,
                defaultValue=None,
                optional=True,
            )
        )
        self.addParameter(
            QgsProcessingParameterBoolean(
                self.CREATE_CONSTRAINT_LINES_ENDS_AT_END_OF_PROFILES,
                self.tr(
                    "Create constraint lines at profiles ends"
                    " (avoid errors when some constraint lines doesn't"
                    " cross all profiles)"
                ),
                defaultValue=False,
            )
        )
        self.addParameter(
            QgsProcessingParameterNumber(
                self.LONG_STEP,
                self.tr("Longitudinal space step (in m)"),
                defaultValue=1.0,
                type=QgsProcessingParameterNumber.Double,
            )
        )
        self.addParameter(
            QgsProcessingParameterNumber(
                self.LAT_STEP,
                self.tr("Lateral space step (in m)"),
                defaultValue=1.0,
                type=QgsProcessingParameterNumber.Double,
            )
        )
        self.addParameter(
            QgsProcessingParameterField(
                self.ATTR_CROSS_SECTION,
                self.tr("Attribute to identify cross-sections"),
                parentLayerParameterName=self.INPUT,
                defaultValue="sec_id",
            )
        )

    def processAlgorithm(self, parameters, context, model_feedback):
        layer = self.parameterAsVectorLayer(parameters, self.INPUT, context)
        feedback = QgsProcessingMultiStepFeedback(3, model_feedback)
        outputs = {}

        # Check if there is two features selected
        if layer.selectedFeatureCount() != 2:
            raise QgsProcessingException(
                self.tr(
                    "Please select exactly two features (profiles) from the input layer!"
                )
            )

        # Extract selected features (profiles) from layer:
        feedback.setProgressText(
            self.tr("Extracting selected features (profiles) from layer:")
        )

        alg_params = {"INPUT": layer, "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT}
        outputs["selected_features"] = processing.run(
            "native:saveselectedfeatures",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

        # Interpolate lines:
        feedback.setCurrentStep(1)
        if feedback.isCanceled():
            return {}
        feedback.setProgressText(self.tr("Lines interpolation:"))

        alg_params = {
            "SECTIONS": outputs["selected_features"]["OUTPUT"],
            "AXIS": parameters[self.AXIS],
            "CONSTRAINT_LINES": parameters[self.CONSTRAINT_LINES],
            "CREATE_CONSTRAINT_LINES_ENDS_AT_END_OF_PROFILES": parameters[
                self.CREATE_CONSTRAINT_LINES_ENDS_AT_END_OF_PROFILES
            ],
            "LONG_STEP": parameters[self.LONG_STEP],
            "LAT_STEP": parameters[self.LAT_STEP],
            "ATTR_CROSS_SECTION": parameters[self.ATTR_CROSS_SECTION],
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["interpolated_lines"] = processing.run(
            "precourlis:interpolate_lines",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

        # Add Interpolate lines to layer:
        feedback.setCurrentStep(2)
        if feedback.isCanceled():
            return {}
        feedback.setProgressText(self.tr("Adding interpolated lines to layer:"))

        # Notes: This algorithm directly edit input layer in place using edit buffer
        layer.startEditing()
        layer.beginEditCommand(self.tr("Added intermediate profiles"))

        temp_layer = context.getMapLayer(outputs["interpolated_lines"]["OUTPUT"])
        for current, feature in enumerate(temp_layer.getFeatures()):
            # Remove first and last duplicates
            if current == 0 or current == (temp_layer.featureCount() - 1):
                continue

            layer.addFeature(feature)

        features_ordered_by_abs_long_request = QgsFeatureRequest().addOrderBy(
            '"abs_long"', ascending=True
        )
        features_ordered_by_abs_long_it = layer.getFeatures(
            features_ordered_by_abs_long_request
        )

        step = 100.0 / layer.featureCount() if layer.featureCount() else 0
        for current, feature in enumerate(features_ordered_by_abs_long_it, start=1):
            # Update sec_id
            feature["sec_id"] = current

            layer.updateFeature(feature)

            # Update the progress bar
            feedback.setProgress(int(current * step))

        layer.endEditCommand()

        # If caching is enabled, a simple canvas refresh might not be sufficient
        # to trigger a redraw and you must clear the cached image for the layer
        if iface.mapCanvas().isCachingEnabled():
            layer.triggerRepaint()
        else:
            iface.mapCanvas().refresh()

        return {}

    def name(self):
        return "add_intermediate_profiles"

    def displayName(self):
        return self.tr("Add intermediate profiles")

    def shortHelpString(self):
        return self.tr(
            "This algorithm add intermediate profiles interpolated between two selected "
            "profiles from the input layer."
            "\n\n"
            "It writes the result directly on the input layer."
            "\n\n"
            "Don't forget to select two features (profiles) from your input profiles layer!"
        )

    def group(self):
        return self.tr("Profiles")

    def groupId(self):
        return "Profiles"

    def createInstance(self):
        return AddIntermediateProfilesAlgorithm()
