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

"""
/***************************************************************************
 PipelineEngineer
                                 A QGIS plugin
 A collection of Pipeline Engineering Tools.
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2025-12-06
        copyright            : (C) 2025 by Pipeline Engineer
        email                : info@pipelineengineer.com.au
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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__ = 'Pipeline Engineer'
__date__ = '2025-12-06'
__copyright__ = '(C) 2025 by Pipeline Engineer'

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

__revision__ = '$Format:%H$'

import os

from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (
    QgsProcessing,
    QgsFeatureSink,
    QgsProcessingAlgorithm,
    QgsProcessingParameterFeatureSource,
    QgsProcessingParameterFeatureSink,
    QgsProcessingParameterField,
    QgsFeatureRequest,
    QgsProcessingParameterBoolean,
    QgsProcessingParameterNumber,
    QgsProcessingParameterVectorLayer,
    QgsProcessingParameterMultipleLayers,
    QgsProcessingParameterEnum,
    QgsProcessingOutputString,
    QgsProcessingException,
    QgsProcessingOutputMultipleLayers,
    QgsProcessingParameterFileDestination
)

from qgis.PyQt.QtGui import QIcon

from ..fluids.logic.fluids import *

class updateFlowRatesAlgorithm(QgsProcessingAlgorithm):

    INPUT = 'INPUT'
    FLOW_RATE_DATA = 'FLOW_RATE_DATA'
    FLUID = 'FLUID'
    AMBIENT_TEMP = 'AMBIENT_TEMP'
    AMBIENT_PRES = 'AMBIENT_PRES'
    ID_FIELD = 'ID_FIELD'
    DATA_ID_FIELD = 'DATA_ID_FIELD'
    SCENARIO_FIELD = 'SCENARIO_FIELD'
    SCENARIO = 'SCENARIO'
    DAT_FLOW_FIELD = 'DAT_FLOW_FIELD'
    FLOW_RATE_FIELD = 'FLOW_RATE_FIELD'
    MASS_FLOW_RATE = 'MASS_FLOW_RATE'
    TIME_UNITS = 'TIME_UNITS'
    VOLUME_UNITS = 'VOLUME_UNITS'
    OUTPUT = 'OUTPUT'
    
    def initAlgorithm(self, config):

        fluids = ["hgas","lgas","hydrogen","methane","water",
                     "biomethane_pure","biomethane_treated","air"]

        script_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
        user_fluids_path = os.path.join(script_dir, 'user_settings','user_fluids.csv')

        user_fluids_df = pd.read_csv(user_fluids_path)
        
        user_fluids_list = user_fluids_df['name'].tolist()
        
        fluids.extend(user_fluids_list)

        volume_units = []
        
        script_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
        vol_units_path = os.path.join(script_dir, 'user_settings','vol_units.csv')
        
        vol_units_df = pd.read_csv(vol_units_path)
        
        user_units_list = vol_units_df['unit'].tolist()
        
        volume_units.extend(user_units_list)

        self.addParameter(
            QgsProcessingParameterFeatureSource(
                self.INPUT,
                self.tr('Layer to be Updated'),
                [QgsProcessing.TypeVector]
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                self.ID_FIELD,
                self.tr('Layer ID Field'),
                parentLayerParameterName=self.INPUT,
                type=QgsProcessingParameterField.Any
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                self.FLOW_RATE_FIELD,
                self.tr('Flow Rate Field to be Updated'),
                parentLayerParameterName=self.INPUT,
                type=QgsProcessingParameterField.Any
            )
        )

        self.addParameter(
            QgsProcessingParameterEnum(
                name=self.FLUID,
                description='Select Fluid',
                options=fluids,
                defaultValue=0  
            )
        )

        self.addParameter(QgsProcessingParameterNumber(
            self.AMBIENT_TEMP, 'Ambient Temperature',
            type=QgsProcessingParameterNumber.Double,
            minValue=0, maxValue=100000, defaultValue=293.15
        ))

        self.addParameter(QgsProcessingParameterNumber(
            self.AMBIENT_PRES, 'Ambient Pressure',
            type=QgsProcessingParameterNumber.Double,
            minValue=0, maxValue=100000, defaultValue=1
        ))

        self.addParameter(
            QgsProcessingParameterFeatureSource(
                self.FLOW_RATE_DATA,
                self.tr('Layer Containing Flow Rates'),
                [QgsProcessing.TypeVector]
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                self.DATA_ID_FIELD,
                self.tr('Flow Rate Data Field Corresponding to ID Field'),
                parentLayerParameterName=self.FLOW_RATE_DATA,
                type=QgsProcessingParameterField.Any
            )
        )
        
        self.addParameter(
            QgsProcessingParameterField(
                self.SCENARIO_FIELD,
                self.tr('Scenario Field'),
                parentLayerParameterName=self.FLOW_RATE_DATA,
                type=QgsProcessingParameterField.Any
            )
        )

        self.addParameter(QgsProcessingParameterNumber(
            self.SCENARIO, 'Scenario',
            type=QgsProcessingParameterNumber.Integer,
            minValue=0, maxValue=100000, defaultValue=1
        ))

        self.addParameter(
            QgsProcessingParameterField(
                self.DAT_FLOW_FIELD,
                self.tr('Flow Rate Field'),
                parentLayerParameterName=self.FLOW_RATE_DATA,
                type=QgsProcessingParameterField.Any
            )
        )

        self.addParameter(
            QgsProcessingParameterEnum(
                name=self.VOLUME_UNITS,
                description='Volume Units',
                options=volume_units,
                defaultValue=0  
            )
        )
        
        self.addParameter(
            QgsProcessingParameterEnum(
                name=self.TIME_UNITS,
                description='Time Units',
                options=['day','hour','minute','second'],
                defaultValue=0  
            )
        )

        self.addParameter(QgsProcessingParameterBoolean(
            self.MASS_FLOW_RATE, 'Flow Rate Data Contains Mass Flow Rates',
            defaultValue=False
        ))

        # Output
        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.OUTPUT,
                self.tr('Flow Rates Updated')
            )
        )

    # -------------------------------------------------------
    # PROCESS
    # -------------------------------------------------------

    def processAlgorithm(self, parameters, context, feedback):

        layer = self.parameterAsVectorLayer(parameters, self.INPUT, context)
        
        layer_name = layer.name()
        
        id_field = self.parameterAsString(parameters, self.ID_FIELD, context)
        flow_rate_field = self.parameterAsString(parameters, self.FLOW_RATE_FIELD, context)
        scenario = self.parameterAsInt(parameters, self.SCENARIO, context)
        
        fluid_index = self.parameterAsInt(parameters, self.FLUID, context)
        ambient_temp = self.parameterAsDouble(parameters, self.AMBIENT_TEMP, context)
        ambient_pres = self.parameterAsDouble(parameters, self.AMBIENT_PRES, context)
        
        data = self.parameterAsVectorLayer(parameters, self.FLOW_RATE_DATA, context)
        data_id_field = self.parameterAsString(parameters, self.DATA_ID_FIELD, context)
        scenario_field = self.parameterAsString(parameters, self.SCENARIO_FIELD, context)
        data_flow_field = self.parameterAsString(parameters, self.DAT_FLOW_FIELD, context)
        
        mass_flow_rate = self.parameterAsBool(parameters, self.MASS_FLOW_RATE, context)
        
        volume_index = self.parameterAsInt(parameters, self.VOLUME_UNITS, context)
        time_index = self.parameterAsInt(parameters, self.TIME_UNITS, context)

        # Fluid

        fluids = ["hgas","lgas","hydrogen","methane","water",
                     "biomethane_pure","biomethane_treated","air"]

        script_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
        user_fluids_path = os.path.join(script_dir, 'user_settings','user_fluids.csv')

        user_fluids_df = pd.read_csv(user_fluids_path)
        
        user_fluids_list = user_fluids_df['name'].tolist()
        
        fluids.extend(user_fluids_list)

        fluid = fluids[time_index]

        fluid_density = get_fluid_parameter(parameter='density',chosen_fluid=fluid,temperature=ambient_temp,pressure=ambient_pres)

        # Volume

        volume_units = []
        
        script_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
        vol_units_path = os.path.join(script_dir, 'user_settings','vol_units.csv')
        
        vol_units_df = pd.read_csv(vol_units_path)
        
        user_units_list = vol_units_df['unit'].tolist()
        
        volume_units.extend(user_units_list)

        volume_unit = volume_units[volume_index]
        
        volume_conversion_factor = vol_units_df.loc[vol_units_df['unit'] == volume_unit, 'conversion_to_m3'].iloc[0]
        
        # Time
        ['day','hour','minute','second']
        if time_index == 0:
        
            time_conversion_factor = 1 /24/60/60
            
        elif time_index == 1:
            
            time_conversion_factor = 1 /60/60
            
        elif time_index == 2:
            
            time_conversion_factor = 1/60
            
        elif time_index == 3:
            
            time_conversion_factor = 1
            
        
        if mass_flow_rate:
            formula = f'"{data_flow_field}" * {volume_conversion_factor} * {time_conversion_factor} * {fluid_density}'
            
        else:
            formula = f'"{data_flow_field}" * {volume_conversion_factor} * {time_conversion_factor}'
        
        # Run your solver
        feedback.pushInfo("Updating flow units…")
        
        expression_extract = processing.run("native:extractbyexpression", 
                                                    {'INPUT':data,
                                                     'EXPRESSION':f'"{scenario_field}" = {scenario}',
                                                     'OUTPUT':'memory:'})['OUTPUT']
        
        field_calc = processing.run("native:fieldcalculator", 
                                                    {'INPUT':expression_extract,
                                                     'FIELD_NAME':f'{flow_rate_field}',
                                                     'FIELD_TYPE':0,
                                                     'FIELD_LENGTH':0,
                                                     'FIELD_PRECISION':0,
                                                     'FORMULA':formula,
                                                     'OUTPUT':'memory:'})['OUTPUT']
        
        dropped = processing.run("native:deletecolumn", 
                                                    {'INPUT':layer,
                                                     'COLUMN':[flow_rate_field],
                                                     'OUTPUT':'memory:'})['OUTPUT']

        field_value = processing.run("native:joinattributestable", 
                                                    {'INPUT':dropped,
                                                     'FIELD':id_field,
                                                     'INPUT_2':field_calc,
                                                     'FIELD_2':data_id_field,
                                                     'FIELDS_TO_COPY':[flow_rate_field],
                                                     'METHOD':1,
                                                     'DISCARD_NONMATCHING':False,
                                                     'PREFIX':'',
                                                     'OUTPUT':'memory:'})['OUTPUT']

        field_value.setName(f'Scenario {scenario} {layer_name}')

        # Create sink with the fields, geometry type and CRS of the output layer
        sink, dest_id = self.parameterAsSink(
            parameters,
            self.OUTPUT,
            context,
            field_value.fields(),
            field_value.wkbType(),
            field_value.sourceCrs()
        )

        # Add resulting features to sink
        for f in field_value.getFeatures():
            if feedback.isCanceled():
                break
            sink.addFeature(f, QgsFeatureSink.FastInsert)

        return {self.OUTPUT: dest_id}
        
    # -------------------------------------------------------

    def name(self):
        return 'update_flow_rates'

    def displayName(self):
        return self.tr('Update Flow Rates')

    def group(self):
        return self.tr(self.groupId())

    def tr(self, string):
        return QCoreApplication.translate('Processing', string)

    def shortHelpString(self):
        return self.tr("Updates flow rates of a selected layer. Will multiply flow rate by fluid density by default, users can select 'Flow Rate Data Contains Mass Flow Rates' to avoid this.")

    def createInstance(self):
        return updateFlowRatesAlgorithm()
