# -*- coding: utf-8 -*-
# this script performs all the task dealing with the valley bottom processing

def valley_bottom_processing(QgsProject, QgsVectorLayer, outputs, QgsProcessing, processing, selected_layer_vb,
                             flag_reverse_vb, cross_section_width, distance_cross_section, export_vb, export_csv_vb,
                             flag_load_temp_vb, name_valley_bottom_blocks, name_valley_bottom_centerline,
                             name_valley_bottom_cross_sections, name_valley_bottom_rk):
    """
    TODO
    Calculation of the blocks of the valley bottom

    :param QgsProject: the instance of the QGIS project
    :type QgsProject: QGIS project
    :param QgsVectorLayer: a QGIS vector layer
    :type QgsVectorLayer: QgsVectorLayer
    :param outputs: a dictionary to store the outputs of the QGIS processes
    :type outputs: dictionary
    :param QgsProcessing: the QGIS processes
    :type QgsProcessing: QgsProcessing
    :param processing: the QGIS processes
    :type processing: QgsProcessing
    :param selected_layer_vb: the polygon vector layer of the bottom valley
    :type selected_layer_vb: vector layer
    :param flag_reverse_vb: a flag to know if we have to reverse the centerline prior to calculate the blocks
    :type flag_reverse_vb: boolean
    :param cross_section_width: the width of the cross-sections to be used (in meters)
    :type cross_section_width: float
    :param distance_cross_section: the distance between two cross-sections (in meters)
    :type distance_cross_section: float
    :param export_vb: the path to export the polygon layer of the blocks of the valley bottom
    :type export_vb: string
    :param export_csv_vb: the path to export the table of the blocks of the valley bottom
    :type export_csv_vb: string
    :param flag_load_temp_vb: a flag in order to load or no the temporary layers
    :type flag_load_temp_vb: boolean
    :param name_valley_bottom_blocks: the name of the layer of the blocks of the bottom valley
    :type name_valley_bottom_blocks: string
    :param name_valley_bottom_centerline: the name of the layer of the centerline of the bottom valley
    :type name_valley_bottom_centerline: string
    :param name_valley_bottom_cross_sections: the name of the layer of the cross-sections of the valley bottom
    :type name_valley_bottom_cross_sections: string
    :param name_valley_bottom_rk: the name of the layer of the layer of the river kilometers
    :type name_valley_bottom_rk: string
    :return: generates and loads the layer (and the table) of the blocks of the valley bottom
    :rtype:
    """

    # We delete the islands of the valley bottom (i.e. the 'holes' in the polygon of the valley bottom layer)
    alg_params = {
        'INPUT': selected_layer_vb,
        'MIN_AREA': 0,
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_noisland'] = processing.run('native:deleteholes', alg_params)

    # We create the centerlines of the valley bottom
    alg_params = {
        'Densify Field': '',
        'Line Spacing': 3,
        'Method': 0,  # Centerlines
        'Polygons': outputs['vb_noisland']['OUTPUT'],
        'Simplify': 0,
        'Simplify Field': '',
        'Trim Field': '',
        'Trim Iterations': 0,
        'Centerlines': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_centerline'] = processing.run('Algorithms:Centerlines', alg_params)

    # We merge the centerline of the valley bottom
    alg_params = {
        'FIELD': [''],
        'INPUT': outputs['vb_centerline']['Centerlines'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_centerline_grouped'] = processing.run('native:dissolve', alg_params)

    # We check if we have to reverse the centerline (if flagReverseVf = True)
    if flag_reverse_vb == True:
        print(flag_reverse_vb)
        alg_params = {
            'INPUT': outputs['vb_centerline_grouped']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['vb_centerline_reversed'] = processing.run('native:reverselinedirection', alg_params)

        # We build the cross-sections along the centerline of the valley bottom
        # it seems that the cross-sections are naturally built by taking the reversed line...
        alg_params = {
            'Centerlines': outputs['vb_centerline_grouped']['OUTPUT'],
            'Densify': 3,
            'Distance': cross_section_width / 2,
            'Samples': distance_cross_section,
            'Output': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['cross_section'] = processing.run('Algorithms:Transects By Distance', alg_params)

        # We build the river kilometer along the centerline of the valley bottom
        alg_params = {
            'DISTANCE': distance_cross_section,
            'END_OFFSET': 0,
            'INPUT': outputs['vb_centerline_reversed']['OUTPUT'],
            'START_OFFSET': distance_cross_section / 2,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['river_km'] = processing.run('native:pointsalonglines', alg_params)

    elif flag_reverse_vb == False:
        alg_params = {
            'INPUT': outputs['vb_centerline_grouped']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['vb_centerline_reversed'] = processing.run('native:reverselinedirection', alg_params)

        # We build the cross-sections along the centerline of the valley bottom
        alg_params = {
            'Centerlines': outputs['vb_centerline_reversed']['OUTPUT'],
            'Densify': 3,
            'Distance': cross_section_width / 2,
            'Samples': distance_cross_section,
            'Output': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['cross_section'] = processing.run('Algorithms:Transects By Distance', alg_params)

        # We build the river kilometer along the centerline of the valley bottom
        alg_params = {
            'DISTANCE': distance_cross_section,
            'END_OFFSET': 0,
            'INPUT': outputs['vb_centerline_grouped']['OUTPUT'],
            'START_OFFSET': distance_cross_section / 2,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['river_km'] = processing.run('native:pointsalonglines', alg_params)

    # We clip the valley bottom according to the cross-sections
    alg_params = {
        'INPUT': outputs['vb_noisland']['OUTPUT'],
        'LINES': outputs['cross_section']['Output'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_blocks'] = processing.run('native:splitwithlines', alg_params)

    # We join the river kilometers to the blocks
    alg_params = {
        'DISCARD_NONMATCHING': False,
        'INPUT': outputs['vb_blocks']['OUTPUT'],
        'JOIN': outputs['river_km']['OUTPUT'],
        'JOIN_FIELDS': ['distance'],
        'METHOD': 0,  # Créer une entité distincte pour chaque entité correspondante (un à plusieurs)
        'PREDICATE': [1],  # contient
        'PREFIX': '',
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_join_blocks_river_km'] = processing.run('native:joinattributesbylocation', alg_params)

    # We only select the blocks where the field 'distance' is not null
    alg_params = {
        'FIELD': 'distance',
        'INPUT': outputs['vb_join_blocks_river_km']['OUTPUT'],
        'OPERATOR': 9,  # is not null
        'METHOD': 0,  # create a new selection
        'VALUE': 'NULL'
    }
    outputs['vb_blocks_selected'] = processing.run('qgis:selectbyattribute', alg_params)

    # Extraction of the selected blocks
    alg_params = {
        'INPUT': outputs['vb_blocks_selected']['OUTPUT'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_blocks_extracted'] = processing.run('native:saveselectedfeatures', alg_params)

    # We delete the 'fid' field
    alg_params = {
        'INPUT': outputs['vb_blocks_extracted']['OUTPUT'],
        'COLUMN': ['fid'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_blocks_no_fid'] = processing.run('native:deletecolumn', alg_params)

    # We calculate a field 'area_m2' in m2
    alg_params = {
        'INPUT': outputs['vb_blocks_no_fid']['OUTPUT'],
        'FIELD_NAME': 'area_m2',
        'FIELD_TYPE': 0,
        'FIELD_LENGTH': 0,
        'FIELD_PRECISION': 0,
        'FORMULA': '$area',
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_blocks_surface'] = processing.run('native:fieldcalculator', alg_params)

    # We calculate a field 'ID'
    alg_params = {
        'INPUT': outputs['vb_blocks_surface']['OUTPUT'],
        'FIELD_NAME': 'ID',
        'FIELD_TYPE': 1,
        'FIELD_LENGTH': 0,
        'FIELD_PRECISION': 0,
        'FORMULA': '$id',
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_blocks_id'] = processing.run('native:fieldcalculator', alg_params)

    # We reorder the columns of the layer containing the valley bottom blocks
    alg_params = {
        'INPUT': outputs['vb_blocks_id']['OUTPUT'],
        'FIELDS_MAPPING': [
            {'expression': '"ID"', 'length': 10, 'name': 'ID', 'precision': 0, 'sub_type': 0, 'type': 4,
             'type_name': 'int8'},
            {'expression': '"area_m2"', 'length': 23, 'name': 'area_m2', 'precision': 15, 'sub_type': 0,
             'type': 6, 'type_name': 'double precision'},
            {'expression': '"distance"', 'length': 23, 'name': 'distance', 'precision': 15, 'sub_type': 0,
             'type': 6, 'type_name': 'double precision'}],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_blocks_reorder_columns'] = processing.run('native:refactorfields', alg_params)

    # We create buffers around the river kilometers points
    alg_params = {
        'DISSOLVE': False,
        'DISTANCE': 2,
        'END_CAP_STYLE': 0,  # Rond
        'INPUT': outputs['river_km']['OUTPUT'],
        'JOIN_STYLE': 0,  # Rond
        'MITER_LIMIT': 2,
        'SEGMENTS': 5,
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_river_km_buffer'] = processing.run('native:buffer', alg_params)

    # We clip the centerline of the valley bottom according to the cross-sections
    alg_params = {
        'INPUT': outputs['vb_centerline_reversed']['OUTPUT'],
        'LINES': outputs['cross_section']['Output'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_centerline_segmented'] = processing.run('native:splitwithlines', alg_params)

    # We join the clipped centerlines to the river kilometer
    alg_params = {
        'DISCARD_NONMATCHING': False,
        'INPUT': outputs['vb_centerline_segmented']['OUTPUT'],
        'JOIN': outputs['vb_river_km_buffer']['OUTPUT'],
        'JOIN_FIELDS': ['distance'],
        'METHOD': 0,  # one to one relationship
        'PREDICATE': [0],  # intersects
        'PREFIX': '',
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_join_centerline_river_km'] = processing.run('native:joinattributesbylocation', alg_params)

    # We calculate the length of each segmented centerline
    alg_params = {
        'FIELD_LENGTH': 0,
        'FIELD_NAME': 'length_m',
        'FIELD_PRECISION': 0,
        'FIELD_TYPE': 0,  # Flottant
        'FORMULA': ' $length ',
        'INPUT': outputs['vb_join_centerline_river_km']['OUTPUT'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_centerline_length'] = processing.run('native:fieldcalculator', alg_params)

    # We remove the useless fields of the segmented centerlines layer
    alg_params = {
        'INPUT': outputs['vb_centerline_length']['OUTPUT'],
        'COLUMN': ['ID', 'Distance', 'RDistance', 'SP_Dist', 'SP_RDist'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_centerline_field_deleted'] = processing.run('native:deletecolumn', alg_params)

    # We join the blocks of the valley bottom to the segmented centerlines
    alg_params = {
        'DISCARD_NONMATCHING': False,
        'FIELD': 'distance',
        'FIELDS_TO_COPY': ['length_m'],
        'FIELD_2': 'distance_2',
        'INPUT': outputs['vb_blocks_reorder_columns']['OUTPUT'],
        'INPUT_2': outputs['vb_centerline_field_deleted']['OUTPUT'],
        'METHOD': 1,  # one-to-one relationship with only matching pairs
        'PREFIX': '',
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_join_blocks_centerline'] = processing.run('native:joinattributestable', alg_params)

    # We calculate the mean width in meter of each block of the valley bottom
    # and we export this layer
    alg_params = {
        'FIELD_LENGTH': 10,
        'FIELD_NAME': 'mean_width_m',
        'FIELD_PRECISION': 3,
        'FIELD_TYPE': 1,  # Flottant
        'FORMULA': 'value = <area_m2> / <length_m>',
        'GLOBAL': '',
        'INPUT': outputs['vb_join_blocks_centerline']['OUTPUT'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_blocks_width'] = processing.run('qgis:advancedpythonfieldcalculator', alg_params)

    # we reorder the layer according to the 'distance' field by ascending order
    alg_params = {
        'INPUT': outputs['vb_blocks_width']['OUTPUT'],
        'EXPRESSION':'"distance"',
        'ASCENDING': True,
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_blocks_reorder'] = processing.run('native:orderbyexpression', alg_params)

    # we remove the 'ID' field
    alg_params = {
        'INPUT': outputs['vb_blocks_reorder']['OUTPUT'],
        'COLUMN': ['ID'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_no_id_field'] = processing.run('native:deletecolumn', alg_params)

    # recalculate an 'ID' field. Now it will be in the same order than the 'Distance'
    alg_params = {
        'FIELD_LENGTH': 3,
        'FIELD_NAME': 'ID',
        'FIELD_PRECISION': 3,
        'FIELD_TYPE': 0,  # Integer
        'FORMULA': 'value = $id',
        'GLOBAL': '',
        'INPUT': outputs['vb_no_id_field']['OUTPUT'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['vb_id_field'] = processing.run('qgis:advancedpythonfieldcalculator', alg_params)

    # we reorder the fields
    alg_params = {
        'INPUT': outputs['vb_id_field']['OUTPUT'],
        'FIELDS_MAPPING': [
            {'expression': '"ID"','length': 0,'name': 'ID','precision': 0,'sub_type': 0,'type': 4,'type_name': 'int8'},
            {'expression': '"distance"', 'length': 0, 'name': 'distance', 'precision': 0, 'sub_type': 0, 'type': 6,'type_name': 'double precision'},
            {'expression': '"area_m2"','length': 0,'name': 'area_m2','precision': 0,'sub_type': 0,'type': 6,'type_name': 'double precision'},
            {'expression': '"length_m"','length': 0,'name': 'length_m','precision': 0,'sub_type': 0,'type': 6,'type_name': 'double precision'},
            {'expression': '"mean_width_m"','length': 0,'name': 'mean_width_m','precision': 0,'sub_type': 0,'type': 6,'type_name': 'double precision'}],
        'OUTPUT': export_vb
    }
    outputs['vb_blocks_reorder_columns'] = processing.run('native:refactorfields', alg_params)

    # if the exported layer is in format Geopackage, we remove the 'fid' column before exporting the attribute table
    # into a spreadsheet
    if 'gpkg' in export_vb:
        alg_params = {
            'INPUT': outputs['vb_blocks_reorder_columns']['OUTPUT'],
            'COLUMN': ['fid'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['vb_blocks_export'] = processing.run('native:deletecolumn', alg_params)
    else:
        outputs['vb_blocks_export'] = outputs['vb_blocks_reorder_columns']

    # We export the attribute table of the layer containing the blocks of the valley bottom into a csv file
    alg_params = {
        'INPUT': outputs['vb_blocks_export']['OUTPUT'],
        'LAYER_OPTIONS': 'SEPARATOR=SEMICOLON',
        'OUTPUT': export_csv_vb
    }
    outputs['vb_blocks_reorder_columns'] = processing.run('native:savefeatures', alg_params)

    # we load the temporary valley bottom layer if so wished
    if flag_load_temp_vb == True:
        QgsProject.instance().addMapLayer(outputs['vb_centerline_segmented']['OUTPUT']).setName(name_valley_bottom_centerline)
        QgsProject.instance().addMapLayer(outputs['cross_section']['Output']).setName(name_valley_bottom_cross_sections)
        QgsProject.instance().addMapLayer(outputs['river_km']['OUTPUT']).setName(name_valley_bottom_rk)

    # We load the final layer of the blocks of the valley bottom
    QgsProject.instance().addMapLayer(QgsVectorLayer(export_vb, name_valley_bottom_blocks, "ogr"))

    return export_vb
