# -*- 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 .logic.running_ac_powerflow import *

class runACPowerFlowAlgorithm(QgsProcessingAlgorithm):

    NET_LAYERS = 'NET_LAYERS'
    ALGORITHM = 'ALGORITHM'
    #CALCULATE_VOLTAGE_ANGLES = 'CALCULATE_VOLTAGE_ANGLES'
    #INIT = 'INIT'
    #MAX_ITERATION = 'MAX_ITERATION'
    #TOLERANCE_MVA = 'TOLERANCE_MVA'
    #TRAFO_MODEL = 'TRAFO_MODEL'
    #TRAFO_LOADING = 'TRAFO_LOADING'
    #ENFORCE_P_LIMS = 'ENFORCE_P_LIMS'
    #ENFORCE_Q_LIMS = 'ENFORCE_Q_LIMS'
    #CHECK_CONNECTIVITY = 'CHECK_CONNECTIVITY'
    #VOLTAGE_DEPEND_LOADS = 'VOLTAGE_DEPEND_LOADS'
    #CONSIDER_LINE_TEMPERATURE = 'CONSIDER_LINE_TEMPERATURE'
    #DISTRIBUTED_SLACK = 'DISTRIBUTED_SLACK'
    #TDPF = 'TDPF'
    #TDPF_DELAY_S = 'TDPF_DELAY_S'
    #LIGHTSIM2GRID = 'LIGHTSIM2GRID'
    #NUMBA = 'NUMBA'
    #SWITCH_RX_RATIO = 'SWITCH_RX_RATIO'
    #DELTA_Q = 'DELTA_Q'
    #TRAFO3W_LOSSES = 'TRAFO3W_LOSSES'
    #V_DEBUG = 'V_DEBUG'
    #INIT_VM_PU = 'INIT_VM_PU'
    #INIT_VA_DEGREE = 'INIT_VA_DEGREE'
    #RECYCLE = 'RECYCLE'
    #NEGLECT_OPEN_SWITCH_BRANCHES = 'NEGLECT_OPEN_SWITCH_BRANCHES'
    #TDPF_UPDATE_R_THETA = 'TDPF_UPDATE_R_THETA'
    LOAD_LAYERS = 'LOAD_LAYERS'
    OUTPUT = 'OUTPUT'
    
    def initAlgorithm(self, config):

        self.addParameter(
            QgsProcessingParameterMultipleLayers(
                name=self.NET_LAYERS,
                description='Network Layers',
                layerType=QgsProcessing.TypeVector
            )
        )

        self.addParameter(
            QgsProcessingParameterEnum(
                name=self.ALGORITHM,
                description='Algorithm',
                options=["nr", "iwamoto_mr","bfsw","gs","fdbx","fdxb"],
                defaultValue=0  
            )
        )

        #self.addParameter(QgsProcessingParameterBoolean(
        #    self.CALCULATE_VOLTAGE_ANGLES, 'Calculate Volatage Angles',
        #    defaultValue=True   
        #))

        #self.addParameter(
        #    QgsProcessingParameterEnum(
        #        name=self.INIT,
        #        description='init',
        #        options=["auto","flat","dc","results"],
        #        defaultValue=0  
        #    )
        #)

        #self.addParameter(QgsProcessingParameterNumber(
        #    self.MAX_ITERATION, 'Maximum Iterations',
        #    type=QgsProcessingParameterNumber.Double,
        #    minValue=0, maxValue=1e5, defaultValue=10000
        #))
        
        #self.addParameter(QgsProcessingParameterNumber(
        #    self.TOLERANCE_MVA, 'MVA Tolerance',
        #    type=QgsProcessingParameterNumber.Double,
        #    minValue=0, maxValue=1, defaultValue=1e-8
        #))

        #self.addParameter(
        #    QgsProcessingParameterEnum(
        #        name=self.TRAFO_MODEL,
        #        description='Transformer Model',
        #        options=["t","pi"],
        #        defaultValue=0  
        #    )
        #)

        #self.addParameter(
        #    QgsProcessingParameterEnum(
        #        name=self.TRAFO_LOADING,
        #        description='Transformer Loading Mode',
        #        options=["current","power"],
        #        defaultValue=0  
        #    )
        #)

        #self.addParameter(QgsProcessingParameterBoolean(
        #    self.ENFORCE_P_LIMS, 'Enforce Generator Active Power Limits',
        #    defaultValue=False   
        #))

        #self.addParameter(QgsProcessingParameterBoolean(
        #    self.ENFORCE_P_LIMS, 'Enforce Generator Reactive Power Limits',
        #    defaultValue=False   
        #))

        #self.addParameter(QgsProcessingParameterBoolean(
        #    self.CHECK_CONNECTIVITY, 'Check Connectivity',
        #    defaultValue=True   
        #))
        
        #self.addParameter(QgsProcessingParameterBoolean(
        #    self.VOLTAGE_DEPEND_LOADS, 'Consider Voltage Dependant Loads',
        #    defaultValue=True   
        #))

        #self.addParameter(QgsProcessingParameterBoolean(
        #    self.CONSIDER_LINE_TEMPERATURE, 'Consider Line Temperature',
        #    defaultValue=False   
        #))

        #self.addParameter(QgsProcessingParameterBoolean(
        #    self.DISTRIBUTED_SLACK, 'Distribute Slack Power',
        #    defaultValue=False   
        #))
        
        #self.addParameter(QgsProcessingParameterBoolean(
        #    self.TDPF, 'Temperature Dependent Power Flow',
        #    defaultValue=False   
        #))

        #self.addParameter(QgsProcessingParameterNumber(
        #    self.TDPF_DELAY_S, 'Temperature Dependent Power Flow Delay (Seconds)',
        #    type=QgsProcessingParameterNumber.Double,
        #    minValue=0, maxValue=0, defaultValue=10000
        #))

        #

        self.addParameter(QgsProcessingParameterBoolean(
            self.LOAD_LAYERS, 'Load Layers?',
            defaultValue=True
        ))

        # Output: list of layer IDs
        self.addParameter(QgsProcessingParameterFileDestination(
                self.OUTPUT,
                'Output GeoPackage',
                fileFilter='GeoPackage (*.gpkg)'
            )
        )

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

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

        # MULTIPLE LAYERS
        layers = self.parameterAsLayerList(parameters, self.NET_LAYERS, context)

        if not layers:
            raise QgsProcessingException("No valid input layers selected.")

        # Extract all parameters
        # Read parameters from user input
        net_layers = self.parameterAsLayerList(parameters, self.NET_LAYERS, context)
        algorithm_index = self.parameterAsInt(parameters, self.ALGORITHM, context)
        #calculate_voltage_angles = self.parameterAsBool(parameters, self.CALCULATE_VOLTAGE_ANGLES, context)
        #init_index = self.parameterAsInt(parameters, self.INIT, context)
        #max_iteration = self.parameterAsDouble(parameters, self.MAX_ITERATION, context)
        #tolerance_mva = self.parameterAsDouble(parameters, self.TOLERANCE_MVA, context)
        #trafo_model_index = self.parameterAsInt(parameters, self.TRAFO_MODEL, context)
        #trafo_loading_index = self.parameterAsInt(parameters, self.TRAFO_LOADING, context)
        #enforce_p_lims_active = self.parameterAsBool(parameters, self.ENFORCE_P_LIMS, context)
        #enforce_p_lims_reactive = self.parameterAsBool(parameters, self.ENFORCE_P_LIMS, context)
        #check_connectivity = self.parameterAsBool(parameters, self.CHECK_CONNECTIVITY, context)
        #voltage_depend_loads = self.parameterAsBool(parameters, self.VOLTAGE_DEPEND_LOADS, context)
        #consider_line_temperature = self.parameterAsBool(parameters, self.CONSIDER_LINE_TEMPERATURE, context)
        #distributed_slack = self.parameterAsBool(parameters, self.DISTRIBUTED_SLACK, context)
        #tdpf = self.parameterAsBool(parameters, self.TDPF, context)
        #tdpf_delay_s = self.parameterAsDouble(parameters, self.TDPF_DELAY_S, context)
        load_layers = self.parameterAsBool(parameters, self.LOAD_LAYERS, context)

        algorithms = ["nr", "iwamoto_mr","bfsw","gs","fdbx","fdxb"]      
        #inits = ["auto","flat","dc","results"]
        #trafo_models = ["t","pi"]
        #trafo_loadings = ["current","power"]
        
        algorithm = algorithms[algorithm_index]
        #init = inits[init_index]
        #trafo_model = trafo_models[trafo_model_index]
        #trafo_loading = trafo_loadings[trafo_loading_index]

        settings = {
                        'algorithm': algorithm,
                        #'calculate_voltage_angles': calculate_voltage_angles,
                        #'init': init,
                        #'max_iteration': max_iteration,
                        #'tolerance_mva': tolerance_mva,
                        #'trafo_model': trafo_model,
                        #'trafo_loading': trafo_loading,
                        #'enforce_p_lims_active': enforce_p_lims_active,
                        #'enforce_p_lims_reactive': enforce_p_lims_reactive,
                        #'check_connectivity': check_connectivity,
                        #'voltage_depend_loads': voltage_depend_loads,
                        #'consider_line_temperature': consider_line_temperature,
                        #'distributed_slack': distributed_slack,
                        #'tdpf': tdpf,
                        #'tdpf_delay_s': tdpf_delay_s
                    }

        gpkg_path = self.parameterAsFileOutput(
                                                parameters,
                                                self.OUTPUT,
                                                context
                                            )

        # Run your solver
        feedback.pushInfo("Running powerflow solver…")

        result_layers = run_ac_powerflow(
            layers=layers,
            args=settings,
            load_layers=load_layers
        )

        processing.run("native:package", 
                    {'LAYERS':result_layers,
                     'OUTPUT':gpkg_path,
                     'OVERWRITE':False,
                     'SAVE_STYLES':True,
                     'SAVE_METADATA':True,
                     'SELECTED_FEATURES_ONLY':False,
                     'EXPORT_RELATED_LAYERS':False}
                   )

        return {}
        
    # -------------------------------------------------------

    def name(self):
        return 'pandapower_ac_powerflow'

    def displayName(self):
        return self.tr('Run AC Powerflow')

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

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

    def shortHelpString(self):
        return self.tr("Runs an AC powerflow for selected layers using pandapower.")

    def createInstance(self):
        return runACPowerFlowAlgorithm()
