# -*- coding: utf-8 -*-

"""
/***************************************************************************
 Geohazard
                                 A QGIS plugin
 Plugin with various tools for landslide analysis and management
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2024-11-29
        copyright            : (C) 2024 by Campus S., Castelli M., Fasciano C., Filipello A.
        email                : andrea.filipello@arpa.piemonte.it
 ***************************************************************************/

/***************************************************************************
 * *
 * 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.                                   *
 * *
 ***************************************************************************/
"""

__author__ = 'Campus S., Castelli M., Fasciano C., Filipello A.'
__date__ = '2024-11-29'
__copyright__ = '(C) 2024 by Campus S., Castelli M., Fasciano C., Filipello A.'

# This will get replaced with a git SHA1 when you do a git archive

__revision__ = '$Format:%H$'

import os
from PyQt5.QtGui import QIcon 

from qgis.core import QgsProcessing
from qgis.core import QgsProcessingAlgorithm
from qgis.core import QgsProcessingMultiStepFeedback
from qgis.core import QgsProcessingParameterRasterLayer
from qgis.core import QgsProcessingParameterNumber
from qgis.core import QgsProcessingParameterVectorLayer
from qgis.core import QgsProcessingParameterFeatureSink
from qgis.core import QgsProcessingParameterDefinition
from qgis.core import QgsCoordinateReferenceSystem
from qgis.core import QgsProcessingContext

import processing

class RockfallDrokaFlow(QgsProcessingAlgorithm):

    def icon(self):
        cmd_folder = os.path.dirname(__file__)
        icon_path = os.path.join(cmd_folder, 'icons', 'droka_flow.png')
        return QIcon(icon_path)

    def initAlgorithm(self, config=None):
        self.addParameter(QgsProcessingParameterRasterLayer('dtm', 'DTM', defaultValue=None))
        self.addParameter(QgsProcessingParameterNumber('cell_size', 'Cell size', type=QgsProcessingParameterNumber.Double, defaultValue=10))
        self.addParameter(QgsProcessingParameterVectorLayer('source_point', 'Source point', types=[QgsProcessing.TypeVectorPoint], defaultValue=None))
        self.addParameter(QgsProcessingParameterNumber('weight_kg', 'Weight (kg)', type=QgsProcessingParameterNumber.Double, defaultValue=100))
        self.addParameter(QgsProcessingParameterNumber('energy_angle_', 'Energy angle (°)', type=QgsProcessingParameterNumber.Double, defaultValue=35))
        
        param = QgsProcessingParameterNumber('mean_of_the_normal_standard_distribution', 'Mean of the normal standard distribution', type=QgsProcessingParameterNumber.Double, defaultValue=0)
        param.setFlags(param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
        self.addParameter(param)
        
        param = QgsProcessingParameterNumber('standard_deviation', 'Standard deviation', type=QgsProcessingParameterNumber.Double, defaultValue=0.1)
        param.setFlags(param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
        self.addParameter(param)
        
        self.addParameter(QgsProcessingParameterFeatureSink('Results_drokaBasic', 'Results_Droka basic', type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=True, supportsAppend=True, defaultValue=None))

    def processAlgorithm(self, parameters, context, model_feedback):
        feedback = QgsProcessingMultiStepFeedback(52, model_feedback)
        results = {}
        outputs = {}

        # Random_1
        alg_params = {
            'EXTENT': parameters['dtm'],
            'MEAN': parameters['mean_of_the_normal_standard_distribution'],
            'OUTPUT_TYPE': 0,
            'PIXEL_SIZE': parameters['cell_size'] + 0.00001,
            'STDDEV': parameters['standard_deviation'],
            'TARGET_CRS': parameters['dtm'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Random_1'] = processing.run('native:createrandomnormalrasterlayer', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Random_3
        alg_params = {
            'EXTENT': parameters['dtm'],
            'MEAN': parameters['mean_of_the_normal_standard_distribution'],
            'OUTPUT_TYPE': 0,
            'PIXEL_SIZE': parameters['cell_size'] + 0.00002,
            'STDDEV': parameters['standard_deviation'],
            'TARGET_CRS': parameters['dtm'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Random_3'] = processing.run('native:createrandomnormalrasterlayer', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # r.drain_1
        alg_params = {
            '-a': False, '-c': True, '-d': False, '-n': False,
            'GRASS_MIN_AREA_PARAMETER': 0.0001,
            'GRASS_OUTPUT_TYPE_PARAMETER': 0,
            'GRASS_RASTER_FORMAT_META': None,
            'GRASS_RASTER_FORMAT_OPT': None,
            'GRASS_REGION_CELLSIZE_PARAMETER': parameters['cell_size'],
            'GRASS_REGION_PARAMETER': parameters['dtm'],
            'GRASS_SNAP_TOLERANCE_PARAMETER': -1,
            'GRASS_VECTOR_DSCO': None,
            'GRASS_VECTOR_EXPORT_NOCAT': False,
            'GRASS_VECTOR_LCO': None,
            'direction': None,
            'input': parameters['dtm'],
            'start_coordinates': None,
            'start_points': parameters['source_point'],
            'drain': QgsProcessing.TEMPORARY_OUTPUT,
            'output': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Rdrain_1'] = processing.run('grass7:r.drain', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # DTM2
        alg_params = {
            'INPUT': [parameters['dtm'], outputs['Random_1']['OUTPUT']],
            'STATISTIC': 2,
            'NODATA_POLICY': 0,
            'REFERENCE_LAYER': parameters['dtm'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Dtm2'] = processing.run('native:cellstatistics', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        feedback.setCurrentStep(4)
        if feedback.isCanceled():
            return {}
        
        # Random_2
        alg_params = {
            'EXTENT': parameters['dtm'],
            'MEAN': parameters['mean_of_the_normal_standard_distribution'],
            'OUTPUT_TYPE': 0,
            'PIXEL_SIZE': parameters['cell_size'] + 0.00004,
            'STDDEV': parameters['standard_deviation'],
            'TARGET_CRS': parameters['dtm'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Random_2'] = processing.run('native:createrandomnormalrasterlayer', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # r.drain_2
        alg_params = {
            '-a': False, '-c': True, '-d': False, '-n': False,
            'GRASS_MIN_AREA_PARAMETER': 0.0001,
            'GRASS_OUTPUT_TYPE_PARAMETER': 0,
            'GRASS_REGION_CELLSIZE_PARAMETER': parameters['cell_size'],
            'GRASS_REGION_PARAMETER': parameters['dtm'],
            'GRASS_SNAP_TOLERANCE_PARAMETER': -1,
            'GRASS_VECTOR_DSCO': None,
            'GRASS_VECTOR_EXPORT_NOCAT': False,
            'GRASS_VECTOR_LCO': None,
            'direction': None,
            'input': outputs['Dtm2']['OUTPUT'],
            'start_coordinates': None,
            'start_points': parameters['source_point'],
            'drain': QgsProcessing.TEMPORARY_OUTPUT,
            'output': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Rdrain_2'] = processing.run('grass7:r.drain', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # quota_start
        alg_params = {
            'COLUMN_PREFIX': 'start_elev',
            'INPUT': parameters['source_point'],
            'RASTERCOPY': parameters['dtm'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Quota_start'] = processing.run('native:rastersampling', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Random_4
        alg_params = {
            'EXTENT': parameters['dtm'],
            'MEAN': parameters['mean_of_the_normal_standard_distribution'],
            'OUTPUT_TYPE': 0,
            'PIXEL_SIZE': parameters['cell_size'] + 0.00003,
            'STDDEV': parameters['standard_deviation'],
            'TARGET_CRS': parameters['dtm'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Random_4'] = processing.run('native:createrandomnormalrasterlayer', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # boulder_mass_raster
        alg_params = {
            'EXTENT': parameters['dtm'],
            'NUMBER': parameters['weight_kg'],
            'OUTPUT_TYPE': 5,
            'PIXEL_SIZE': parameters['cell_size'],
            'TARGET_CRS': parameters['dtm'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Boulder_mass_raster'] = processing.run('native:createconstantrasterlayer', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # DTM4
        alg_params = {
            'INPUT': [parameters['dtm'], outputs['Random_3']['OUTPUT']],
            'STATISTIC': 2,
            'NODATA_POLICY': 0,
            'REFERENCE_LAYER': parameters['dtm'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Dtm4'] = processing.run('native:cellstatistics', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # DTM5
        alg_params = {
            'INPUT': [parameters['dtm'], outputs['Random_4']['OUTPUT']],
            'STATISTIC': 2,
            'NODATA_POLICY': 0,
            'REFERENCE_LAYER': parameters['dtm'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Dtm5'] = processing.run('native:cellstatistics', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # vector_2
        alg_params_p2p = {
            'FIELD_NAME': 'valore_pixel',
            'INPUT_RASTER': outputs['Rdrain_2']['output'], # <-- MODIFICATO: 'output' invece di 'drain'
            'RASTER_BAND': 1,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        points_drain_2 = processing.run('native:pixelstopoints', alg_params_p2p, context=context, feedback=feedback, is_child_algorithm=True)
        
        alg_params = {
            'COLUMN_PREFIX': 'quota_2',
            'INPUT': points_drain_2['OUTPUT'],
            'RASTERCOPY': outputs['Dtm2']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Vector_2'] = processing.run('native:rastersampling', alg_params, context=context, feedback=feedback, is_child_algorithm=True)        

        # DTM3
        alg_params = {
            'INPUT': [parameters['dtm'], outputs['Random_2']['OUTPUT']],
            'STATISTIC': 2,
            'NODATA_POLICY': 0,
            'REFERENCE_LAYER': parameters['dtm'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Dtm3'] = processing.run('native:cellstatistics', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        feedback.setCurrentStep(14)
        if feedback.isCanceled():
            return {}
            
        # vector_1
        alg_params_p2p = {
            'FIELD_NAME': 'valore_pixel',
            'INPUT_RASTER': outputs['Rdrain_1']['output'], # <-- MODIFICATO: 'output' invece di 'drain'
            'RASTER_BAND': 1,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        points_drain_1 = processing.run('native:pixelstopoints', alg_params_p2p, context=context, feedback=feedback, is_child_algorithm=True)

        alg_params = {
            'COLUMN_PREFIX': 'quota_1',
            'INPUT': points_drain_1['OUTPUT'],
            'RASTERCOPY': parameters['dtm'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Vector_1'] = processing.run('native:rastersampling', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Attribute union_1
        alg_params = {
            'DISCARD_NONMATCHING': False,
            'FIELDS_TO_COPY': ['start_elev1'],
            'INPUT': outputs['Vector_1']['OUTPUT'],
            'INPUT_2': outputs['Quota_start']['OUTPUT'],
            'MAX_DISTANCE': None,
            'NEIGHBORS': 10000,
            'PREFIX': None,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['AttributeUnion_1'] = processing.run('native:joinbynearest', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Calculate field shadow_1
        alg_params = {
            'FIELD_LENGTH': 10,
            'FIELD_NAME': 'shadow_1',
            'FIELD_PRECISION': 2,
            'FIELD_TYPE': 0,
            'FORMULA': parameters['energy_angle_'],
            'INPUT': outputs['AttributeUnion_1']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['CalculateFieldShadow_1'] = processing.run('native:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Delta calculation_1
        alg_params = {
            'FIELD_LENGTH': 10,
            'FIELD_NAME': 'cone_1',
            'FIELD_PRECISION': 2,
            'FIELD_TYPE': 0,
            'FORMULA': '( "start_elev1" - (tan(radians( "shadow_1" )) * "distance" )) - "quota_11"',
            'INPUT': outputs['CalculateFieldShadow_1']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['DeltaCalculation_1'] = processing.run('native:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)            

        # r.drain_3
        alg_params = {
            '-a': False, '-c': True, '-d': False, '-n': False,
            'GRASS_MIN_AREA_PARAMETER': 0.0001,
            'GRASS_OUTPUT_TYPE_PARAMETER': 0,
            'GRASS_REGION_CELLSIZE_PARAMETER': parameters['cell_size'],
            'GRASS_REGION_PARAMETER': parameters['dtm'],
            'GRASS_SNAP_TOLERANCE_PARAMETER': -1,
            'GRASS_VECTOR_DSCO': None,
            'GRASS_VECTOR_EXPORT_NOCAT': False,
            'GRASS_VECTOR_LCO': None,
            'direction': None,
            'input': outputs['Dtm3']['OUTPUT'],
            'start_coordinates': None,
            'start_points': parameters['source_point'],
            'drain': QgsProcessing.TEMPORARY_OUTPUT,
            'output': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Rdrain_3'] = processing.run('grass7:r.drain', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # r.drain_5
        alg_params = {
            '-a': False, '-c': True, '-d': False, '-n': False,
            'GRASS_MIN_AREA_PARAMETER': 0.0001,
            'GRASS_OUTPUT_TYPE_PARAMETER': 0,
            'GRASS_REGION_CELLSIZE_PARAMETER': parameters['cell_size'],
            'GRASS_REGION_PARAMETER': parameters['dtm'],
            'GRASS_SNAP_TOLERANCE_PARAMETER': -1,
            'GRASS_VECTOR_DSCO': None,
            'GRASS_VECTOR_EXPORT_NOCAT': False,
            'GRASS_VECTOR_LCO': None,
            'direction': None,
            'input': outputs['Dtm5']['OUTPUT'],
            'start_coordinates': None,
            'start_points': parameters['source_point'],
            'drain': QgsProcessing.TEMPORARY_OUTPUT,
            'output': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Rdrain_5'] = processing.run('grass7:r.drain', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # r.drain_4
        alg_params = {
            '-a': False, '-c': True, '-d': False, '-n': False,
            'GRASS_MIN_AREA_PARAMETER': 0.0001,
            'GRASS_OUTPUT_TYPE_PARAMETER': 0,
            'GRASS_REGION_CELLSIZE_PARAMETER': parameters['cell_size'],
            'GRASS_REGION_PARAMETER': parameters['dtm'],
            'GRASS_SNAP_TOLERANCE_PARAMETER': -1,
            'GRASS_VECTOR_DSCO': None,
            'GRASS_VECTOR_EXPORT_NOCAT': False,
            'GRASS_VECTOR_LCO': None,
            'direction': None,
            'input': outputs['Dtm4']['OUTPUT'],
            'start_coordinates': None,
            'start_points': parameters['source_point'],
            'drain': QgsProcessing.TEMPORARY_OUTPUT,
            'output': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Rdrain_4'] = processing.run('grass7:r.drain', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Attribute union_2
        alg_params = {
            'DISCARD_NONMATCHING': False,
            'FIELDS_TO_COPY': ['start_elev1'],
            'INPUT': outputs['Vector_2']['OUTPUT'],
            'INPUT_2': outputs['Quota_start']['OUTPUT'],
            'MAX_DISTANCE': None,
            'NEIGHBORS': 10000,
            'PREFIX': None,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['AttributeUnion_2'] = processing.run('native:joinbynearest', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # vector_4
        alg_params_p2p = {
            'FIELD_NAME': 'valore_pixel',
            'INPUT_RASTER': outputs['Rdrain_4']['output'], # <-- MODIFICATO: 'output' invece di 'drain'
            'RASTER_BAND': 1,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        points_drain_4 = processing.run('native:pixelstopoints', alg_params_p2p, context=context, feedback=feedback, is_child_algorithm=True)
        
        alg_params = {
            'COLUMN_PREFIX': 'quota_4',
            'INPUT': points_drain_4['OUTPUT'],
            'RASTERCOPY': outputs['Dtm4']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Vector_4'] = processing.run('native:rastersampling', alg_params, context=context, feedback=feedback, is_child_algorithm=True)    

        # vector_5
        alg_params_p2p = {
            'FIELD_NAME': 'valore_pixel',
            'INPUT_RASTER': outputs['Rdrain_5']['output'], # <-- MODIFICATO: 'output' invece di 'drain'
            'RASTER_BAND': 1,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        points_drain_5 = processing.run('native:pixelstopoints', alg_params_p2p, context=context, feedback=feedback, is_child_algorithm=True)

        alg_params = {
            'COLUMN_PREFIX': 'quota_5',
            'INPUT': points_drain_5['OUTPUT'],
            'RASTERCOPY': outputs['Dtm5']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Vector_5'] = processing.run('native:rastersampling', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

      # vector_3
        alg_params_p2p = {
            'FIELD_NAME': 'valore_pixel',
            'INPUT_RASTER': outputs['Rdrain_3']['output'], # <-- MODIFICATO: 'output' invece di 'drain'
            'RASTER_BAND': 1,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        points_drain_3 = processing.run('native:pixelstopoints', alg_params_p2p, context=context, feedback=feedback, is_child_algorithm=True)
      
        alg_params = {
            'COLUMN_PREFIX': 'quota_3',
            'INPUT': points_drain_3['OUTPUT'],
            'RASTERCOPY': outputs['Dtm3']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Vector_3'] = processing.run('native:rastersampling', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        # Calculate field shadow2
        alg_params = {
            'FIELD_LENGTH': 10,
            'FIELD_NAME': 'shadow_2',
            'FIELD_PRECISION': 2,
            'FIELD_TYPE': 0,
            'FORMULA': parameters['energy_angle_'],
            'INPUT': outputs['AttributeUnion_2']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['CalculateFieldShadow2'] = processing.run('native:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        # Attribute union_5
        alg_params = {
            'DISCARD_NONMATCHING': False,
            'FIELDS_TO_COPY': ['start_elev1'],
            'INPUT': outputs['Vector_5']['OUTPUT'],
            'INPUT_2': outputs['Quota_start']['OUTPUT'],
            'MAX_DISTANCE': None,
            'NEIGHBORS': 10000,
            'PREFIX': None,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['AttributeUnion_5'] = processing.run('native:joinbynearest', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Attribute union_3
        alg_params = {
            'DISCARD_NONMATCHING': False,
            'FIELDS_TO_COPY': ['start_elev1'],
            'INPUT': outputs['Vector_3']['OUTPUT'],
            'INPUT_2': outputs['Quota_start']['OUTPUT'],
            'MAX_DISTANCE': None,
            'NEIGHBORS': 10000,
            'PREFIX': None,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['AttributeUnion_3'] = processing.run('native:joinbynearest', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        # Attribute union_4
        alg_params = {
            'DISCARD_NONMATCHING': False,
            'FIELDS_TO_COPY': ['start_elev1'],
            'INPUT': outputs['Vector_4']['OUTPUT'],
            'INPUT_2': outputs['Quota_start']['OUTPUT'],
            'MAX_DISTANCE': None,
            'NEIGHBORS': 10000,
            'PREFIX': None,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['AttributeUnion_4'] = processing.run('native:joinbynearest', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        # Calculate field shadow5
        alg_params = {
            'FIELD_LENGTH': 10,
            'FIELD_NAME': 'shadow_5',
            'FIELD_PRECISION': 2,
            'FIELD_TYPE': 0,
            'FORMULA': parameters['energy_angle_'],
            'INPUT': outputs['AttributeUnion_5']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['CalculateFieldShadow5'] = processing.run('native:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)
        
        feedback.setCurrentStep(29)
        if feedback.isCanceled():
            return {}

        # Delta calculation_5
        alg_params = {
            'FIELD_LENGTH': 10,
            'FIELD_NAME': 'cone_5',
            'FIELD_PRECISION': 2,
            'FIELD_TYPE': 0,
            'FORMULA': '( "start_elev1" - (tan(radians( "shadow_5" )) * "distance" )) - "quota_51"',
            'INPUT': outputs['CalculateFieldShadow5']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['DeltaCalculation_5'] = processing.run('native:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Delta calculation_2
        alg_params = {
            'FIELD_LENGTH': 10,
            'FIELD_NAME': 'cone_2',
            'FIELD_PRECISION': 2,
            'FIELD_TYPE': 0,
            'FORMULA': '( "start_elev1" - (tan(radians( "shadow_2" )) * "distance" )) - "quota_21"',
            'INPUT': outputs['CalculateFieldShadow2']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['DeltaCalculation_2'] = processing.run('native:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        # Calculate field shadow3
        alg_params = {
            'FIELD_LENGTH': 10,
            'FIELD_NAME': 'shadow_3',
            'FIELD_PRECISION': 2,
            'FIELD_TYPE': 0,
            'FORMULA': parameters['energy_angle_'],
            'INPUT': outputs['AttributeUnion_3']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['CalculateFieldShadow3'] = processing.run('native:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        # Calculate field shadow4
        alg_params = {
            'FIELD_LENGTH': 10,
            'FIELD_NAME': 'shadow_4',
            'FIELD_PRECISION': 2,
            'FIELD_TYPE': 0,
            'FORMULA': parameters['energy_angle_'],
            'INPUT': outputs['AttributeUnion_4']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['CalculateFieldShadow4'] = processing.run('native:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Delta calculation_4
        alg_params = {
            'FIELD_LENGTH': 10,
            'FIELD_NAME': 'cone_4',
            'FIELD_PRECISION': 2,
            'FIELD_TYPE': 0,
            'FORMULA': '( "start_elev1" - (tan(radians( "shadow_4" )) * "distance" )) - "quota_41"',
            'INPUT': outputs['CalculateFieldShadow4']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['DeltaCalculation_4'] = processing.run('native:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Delta calculation_3
        alg_params = {
            'FIELD_LENGTH': 10,
            'FIELD_NAME': 'cone_3',
            'FIELD_PRECISION': 2,
            'FIELD_TYPE': 0,
            'FORMULA': '( "start_elev1" - (tan(radians( "shadow_3" )) * "distance" )) - "quota_31"',
            'INPUT': outputs['CalculateFieldShadow3']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['DeltaCalculation_3'] = processing.run('native:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        # Vectors fusion
        alg_params = {
            'CRS': parameters['dtm'].crs() if hasattr(parameters['dtm'], 'crs') else QgsCoordinateReferenceSystem('EPSG:32632'),
            'LAYERS': [outputs['DeltaCalculation_1']['OUTPUT'], outputs['DeltaCalculation_4']['OUTPUT'], outputs['DeltaCalculation_5']['OUTPUT'], outputs['DeltaCalculation_2']['OUTPUT'], outputs['DeltaCalculation_3']['OUTPUT']],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['VectorsFusion'] = processing.run('native:mergevectorlayers', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # spatial index_fusion
        alg_params = {
            'INPUT': outputs['VectorsFusion']['OUTPUT']
        }
        outputs['SpatialIndex_fusion'] = processing.run('native:createspatialindex', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Field boulder_mass
        alg_params = {
            'COLUMN_PREFIX': 'mass_',
            'INPUT': outputs['SpatialIndex_fusion']['OUTPUT'],
            'RASTERCOPY': outputs['Boulder_mass_raster']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['FieldBoulder_mass'] = processing.run('native:rastersampling', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Energy
        alg_params = {
            'FIELD_LENGTH': 10,
            'FIELD_NAME': 'energy',
            'FIELD_PRECISION': 2,
            'FIELD_TYPE': 0,
            'FORMULA': 'CASE\r\nWHEN "cone_1" IS NOT NULL THEN ("mass_1" *9.8*"cone_1"/1000)\r\nWHEN "cone_2" IS NOT NULL THEN ("mass_1" *9.8*"cone_2"/1000)\r\nWHEN "cone_3" IS NOT NULL THEN ("mass_1" *9.8*"cone_3"/1000)\r\nWHEN "cone_4" IS NOT NULL THEN ("mass_1" *9.8*"cone_4"/1000)\r\nELSE ("mass_1" *9.8*"cone_5"/1000)\r\nEND',
            'INPUT': outputs['FieldBoulder_mass']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Energy'] = processing.run('native:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Energy>0
        alg_params = {
            'EXPRESSION': '"energy">0',
            'INPUT': outputs['Energy']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Energy0'] = processing.run('native:extractbyexpression', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Velocity
        alg_params = {
            'FIELD_LENGTH': 10,
            'FIELD_NAME': 'velocity',
            'FIELD_PRECISION': 3,
            'FIELD_TYPE': 0,
            'FORMULA': 'CASE\r\nWHEN "cone_1" IS NOT NULL THEN (sqrt(2*9.81* "cone_1"))\r\nWHEN "cone_2" IS NOT NULL THEN (sqrt(2*9.81* "cone_2"))\r\nWHEN "cone_3" IS NOT NULL THEN (sqrt(2*9.81* "cone_3"))\r\nWHEN "cone_4" IS NOT NULL THEN (sqrt(2*9.81* "cone_4"))\r\nELSE (sqrt(2*9.81* "cone_5"))\r\nEND',
            'INPUT': outputs['Energy0']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Velocity'] = processing.run('native:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Velocity_spatial
        alg_params = {
            'INPUT': outputs['Velocity']['OUTPUT']
        }
        outputs['Velocity_spatial'] = processing.run('native:createspatialindex', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Grid
        alg_params = {
            'CRS': parameters['dtm'],
            'EXTENT': outputs['Velocity']['OUTPUT'],
            'HOVERLAY': 0,
            'HSPACING': parameters['cell_size'],
            'TYPE': 0,
            'VOVERLAY': 0,
            'VSPACING': parameters['cell_size'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Grid'] = processing.run('native:creategrid', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Grid_spatial
        alg_params = {
            'INPUT': outputs['Grid']['OUTPUT']
        }
        outputs['Grid_spatial'] = processing.run('native:createspatialindex', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Recap_energy
        alg_params = {
            'DISCARD_NONMATCHING': True,
            'INPUT': outputs['Grid_spatial']['OUTPUT'],
            'JOIN': outputs['Velocity_spatial']['OUTPUT'],
            'JOIN_FIELDS': ['energy'],
            'PREDICATE': [2],
            'SUMMARIES': [2, 3, 6],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Recap_energy'] = processing.run('native:joinbylocationsummary', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Recap_velocity
        alg_params = {
            'DISCARD_NONMATCHING': True,
            'INPUT': outputs['Recap_energy']['OUTPUT'],
            'JOIN': outputs['Velocity_spatial']['OUTPUT'],
            'JOIN_FIELDS': ['velocity'],
            'PREDICATE': [2],
            'SUMMARIES': [2, 3, 6],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Recap_velocity'] = processing.run('native:joinbylocationsummary', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # Delete fields
        alg_params = {
            'COLUMN': ['left', 'top', 'right', 'bottom', 'row_index', 'col_index'],
            'INPUT': outputs['Recap_velocity']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['DeleteFields'] = processing.run('native:deletecolumn', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # 1. Buffer
        alg_params = {
            'DISSOLVE': False,
            'DISTANCE': parameters['cell_size'] * 3,
            'END_CAP_STYLE': 0,
            'INPUT': outputs['DeleteFields']['OUTPUT'],
            'JOIN_STYLE': 0,
            'MITER_LIMIT': 2,
            'SEGMENTS': 1,
            '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(48)
        if feedback.isCanceled():
            return {}

        # 2. Dissolvi
        alg_params = {
            'FIELD': [''],
            'INPUT': outputs['Buffer']['OUTPUT'],
            'SEPARATE_DISJOINT': False,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Dissolvi'] = processing.run('native:dissolve', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # 3. Da multi parte a parti singole
        alg_params = {
            'INPUT': outputs['Dissolvi']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['DaMultiParteAPartiSingole'] = processing.run('native:multiparttosingleparts', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # 4. Estrai 1
        alg_params = {
            'INPUT': outputs['DaMultiParteAPartiSingole']['OUTPUT'],
            'INTERSECT': parameters['source_point'],
            'PREDICATE': [0],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['Estrai1'] = processing.run('native:extractbylocation', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

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

        # 5. Estrai 2
        alg_params = {
            'INPUT': outputs['DeleteFields']['OUTPUT'],
            'INTERSECT': outputs['Estrai1']['OUTPUT'],
            'PREDICATE': [4, 6],
            'OUTPUT': parameters['Results_drokaBasic']
        }
        outputs['Estrai2'] = processing.run('native:extractbylocation', alg_params, context=context, feedback=feedback, is_child_algorithm=True)
        
        # --- NOME DINAMICO IN BASE ALL'ENERGY ANGLE ---
        energy_angle_val = parameters.get('energy_angle_', '')
        output_name = f"droka_flow_output_E{energy_angle_val}_pulito"
        output_layer = outputs['Estrai2']['OUTPUT']
        
        context.addLayerToLoadOnCompletion(
            output_layer,
            QgsProcessingContext.LayerDetails(output_name, context.project(), 'Results_drokaBasic')
        )

        results['Results_drokaBasic'] = output_layer
        return results        

    def name(self):
        return 'Rockfall - Droka flow'

    def displayName(self):
        return 'Rockfall - Droka flow'

    def group(self):
        return 'Landslide'

    def groupId(self):
        return 'Landslide'


    def shortHelpString(self):
        return """<html><body><p><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14pt; font-weight:600;">DROKA FLOW</span></p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Droka_flow employs a hydrological approach to simulate potential rockfall paths by tracing the line of maximum slope for each source point. The path terminates when the inclination of the line passing through the source point is lower than the inclination of the energy line, defined by the user. </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Each analysis includes five simulations from each source point. These simulations adjust DTM elevation using a Monte Carlo approach within a user-defined standard deviation around the original DTM elevation values, assuming a simplified normal distribution of elevation for each cell. The mean corresponds to the original DTM height, while the user-defined standard deviation is constant across cells. This approach provides a simplified representation of the variability in rockfall trajectories due to factors like ground impact, bouncing, and obstacles (e.g., rocks, trees, or structures).</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The analysis result is a vector point layer, containing one point for each DTM cell covered by the simulated paths.</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">For each point,  the height difference between the energy line and the ground is calculated, leading to block velocity and kinetic energy calculations.</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Source points are obtained from the DTM while the energy angle and the standard deviation of the distribution of cells elevation are specific to each case study. In particular, the standard deviation should be related to the DTM size.</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The output is a vector layer containing:</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- Id</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- Energy min, max, mean (kJ)</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- Velocity min, max, mean (m/s)</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Frequency data is omitted here due to the limited number of paths per source point, so energy count and percent fields are not included.</p></body></html></p>
<h2>Parametri in ingresso
</h2>
<h3>DTM</h3>
<p>Raster layer of the DTM</p>
<h3>Cell size</h3>
<p>Dimension of the DTM cell (m)</p>
<h3>Source point</h3>
<p>Vector layer of the source points</p>
<h3>Weight (kg)</h3>
<p>Weight of the block (kg)</p>
<h3>Energy angle (°)</h3>
<p>Energy angle ϕp = cost (°)</p>
<h3>Standard deviation</h3>
<p>Standard deviation of the normal distribution of DTM elevation (m)</p>
<h2>Esempi</h2>
<p><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;">
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></p><br><p align="right">Autore della guida: Claudio Fasciano</p></body></html>"""

    def createInstance(self):
        return RockfallDrokaFlow()