/***************************************************************************
                         qgsprocessingmodelalgorithm.h
                         -----------------------------
    begin                : June 2017
    copyright            : (C) 2017 by Nyall Dawson
    email                : nyall dot dawson at gmail dot com
 ***************************************************************************/

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

#ifndef QGSPROCESSINGMODELALGORITHM_H
#define QGSPROCESSINGMODELALGORITHM_H

#include "qgis.h"
#include "qgis_core.h"
#include "qgsprocessingalgorithm.h"
#include "qgsprocessingmodelchildalgorithm.h"
#include "qgsprocessingmodelchilddependency.h"
#include "qgsprocessingmodelchildparametersource.h"
#include "qgsprocessingmodelgroupbox.h"
#include "qgsprocessingmodelparameter.h"

///@cond NOT_STABLE

/**
 * \class QgsProcessingModelAlgorithm
 * \ingroup core
 * \brief Model based algorithm with processing.
 */
class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
{
    Q_GADGET

  public:
    /**
     * Constructor for QgsProcessingModelAlgorithm.
     */
    QgsProcessingModelAlgorithm( const QString &name = QString(), const QString &group = QString(), const QString &groupId = QString() );

    void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override; //#spellok

    Qgis::ProcessingAlgorithmFlags flags() const override;
    QString name() const override;
    QString displayName() const override;
    QString group() const override;
    QString groupId() const override;
    QIcon icon() const override;
    QString svgIconPath() const override;
    QString shortHelpString() const override;
    QString shortDescription() const override;
    QString helpUrl() const override;

    bool canExecute( QString *errorMessage SIP_OUT = nullptr ) const override;
    QString asPythonCommand( const QVariantMap &parameters, QgsProcessingContext &context ) const override;
    QgsExpressionContext createExpressionContext( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeatureSource *source = nullptr ) const override;

    /**
     * Sets the model \a name.
     * \see name()
     */
    void setName( const QString &name );

    /**
     * Sets the model \a group.
     * \see group()
     */
    void setGroup( const QString &group );

    /**
     * Validates the model, returning TRUE if all child algorithms in the model are valid.
     *
     * \param issues will be set to a list of issues encountered during the validation
     * \returns TRUE if the child is valid
     * \since QGIS 3.14
     */
    bool validate( QStringList &issues SIP_OUT ) const;

    /**
     * Returns the map of child algorithms contained in the model. The keys
     * are the child algorithm ids (see QgsProcessingModelAlgorithm::ChildAlgorithm::childId()).
     * \see childAlgorithm()
     * \see setChildAlgorithms()
     * \see addChildAlgorithm()
     */
    QMap<QString, QgsProcessingModelChildAlgorithm> childAlgorithms() const;

    /**
     * Sets the map of child algorithms contained in the model. The keys
     * are the child algorithm ids (see QgsProcessingModelAlgorithm::ChildAlgorithm::childId()).
     * All existing child algorithms will be replaced.
     * \see childAlgorithms()
     * \see childAlgorithm()
     * \see setChildAlgorithm()
     * \see addChildAlgorithm()
     */
    void setChildAlgorithms( const QMap<QString, QgsProcessingModelChildAlgorithm> &childAlgorithms );

    /**
     * Sets the child \a algorithm within the model. If a child algorithm already
     * exists in the model with the same child ID then that algorithm will be replaced.
     * \see addChildAlgorithm()
     * \see setChildAlgorithms()
     */
    void setChildAlgorithm( const QgsProcessingModelChildAlgorithm &algorithm );

    /**
     * Adds a new child \a algorithm to the model. If a child algorithm already exists
     * in the model with the same child ID then \a algorithm will be assigned a new
     * autogenerated unique ID.
     * The assigned child ID will be returned.
     * \see childAlgorithms()
     * \see childAlgorithm()
     * \see setChildAlgorithm()
     * \see setChildAlgorithms()
     */
    QString addChildAlgorithm( QgsProcessingModelChildAlgorithm &algorithm );

    /**
     * Returns the child algorithm with matching \a id. If no child algorithm exists with
     * this ID a new algorithm will be added to the model and returned.
     * \see addChildAlgorithm()
     * \see childAlgorithms()
     */
    QgsProcessingModelChildAlgorithm &childAlgorithm( const QString &id );

    /**
     * Attempts to remove the child algorithm with matching \a id.
     * Returns TRUE if the algorithm could be removed, or FALSE
     * if the algorithm could not be removed (e.g. due to other
     * child algorithms depending on it).
     * \see deactivateChildAlgorithm()
     */
    bool removeChildAlgorithm( const QString &id );

    /**
     * Deactivates the child algorithm with matching \a id.
     * All other child algorithms which depend on the child
     * algorithm will also be deactivated.
     * \see removeChildAlgorithm()
     * \see activateChildAlgorithm()
     */
    void deactivateChildAlgorithm( const QString &id );

    /**
     * Attempts to activate the child algorithm with matching \a id.
     * If any child algorithms on which the child depends are not active,
     * then the child will not be activated and FALSE will be returned.
     * \see deactivateChildAlgorithm()
     */
    bool activateChildAlgorithm( const QString &id );

    /**
     * Returns a list of the child algorithm IDs depending on the child
     * algorithm with the specified \a childId.
     *
     * Optionally, a specific conditional branch which is created by the child algorithm
     * can be specified in order to restrict the returned list to algorithms which depend
     * on this specific branch.
     *
     * \see dependsOnChildAlgorithms()
     */
    QSet< QString > dependentChildAlgorithms( const QString &childId, const QString &conditionalBranch = QString() ) const;

    /**
     * Returns a list of the child algorithm IDs on which the child
     * algorithm with the specified \a childId depends upon.
     * \see dependentChildAlgorithms()
     */
    QSet< QString > dependsOnChildAlgorithms( const QString &childId ) const;

    /**
     * Returns details of available dependencies for the child algorithm with matching id.
     *
     * \since QGIS 3.14
     */
    QList< QgsProcessingModelChildDependency > availableDependenciesForChildAlgorithm( const QString &childId ) const;

    /**
     * Validates the child algorithm with matching ID, returning TRUE if
     * all mandatory inputs to the algorithm have valid values.
     *
     * \param childId ID for child to validate
     * \param issues will be set to a list of issues encountered during the validation
     * \returns TRUE if the child is valid
     * \since QGIS 3.14
     */
    bool validateChildAlgorithm( const QString &childId, QStringList &issues SIP_OUT ) const;

    /**
     * Adds a new parameter to the model, with the specified \a definition and graphical \a component.
     * Ownership of \a definition is transferred to the model.
     * \see updateModelParameter()
     * \see removeModelParameter()
     */
    void addModelParameter( QgsProcessingParameterDefinition *definition SIP_TRANSFER, const QgsProcessingModelParameter &component );

    /**
     * Replaces the definition of an existing parameter (by parameter name) with a new \a definition. Ownership of
     * \a definition is transferred to the model, and any existing parameter is deleted.
     * \see addModelParameter()
     * \see removeModelParameter()
     */
    void updateModelParameter( QgsProcessingParameterDefinition *definition SIP_TRANSFER );

    /**
     * Removes an existing model parameter by \a name. The definition of the matching parameter
     * is deleted.
     * \see addModelParameter()
     * \see updateModelParameter()
     */
    void removeModelParameter( const QString &name );

    /**
     * Changes a model parameter's internal name from \a oldName to \a newName.
     *
     * This method will automatically update all model components to relink using the new name.
     *
     * \since QGIS 3.26
     */
    void changeParameterName( const QString &oldName, const QString &newName );

    /**
     * Returns TRUE if any child algorithms depend on the model parameter
     * with the specified \a name.
     * \see otherParametersDependOnParameter()
     */
    bool childAlgorithmsDependOnParameter( const QString &name ) const;

    /**
     * Returns TRUE if any other model parameters depend on the parameter
     * with the specified \a name (e.g. field parameters where \a name
     * is the parent layer parameter).
     * \see childAlgorithmsDependOnParameter()
     */
    bool otherParametersDependOnParameter( const QString &name ) const;

    /**
     * Returns the map of parameter components used by the model. The keys
     * should match the algorithm's parameter names (see parameterDefinitions() ).
     * \see setParameterComponent()
     * \see parameterComponent()
     */
    QMap<QString, QgsProcessingModelParameter> parameterComponents() const;

    /**
     * Sets the map of parameter components used by the model. The keys
     * should match the algorithm's parameter names (see parameterDefinitions() ).
     * All existing parameter components will be replaced.
     * \see parameterComponents()
     * \see setParameterComponent()
     * \see parameterComponent()
     */
    void setParameterComponents( const QMap<QString, QgsProcessingModelParameter> &parameterComponents );

    /**
     * Sets a parameter \a component for the model. If a parameter component already
     * exists in the model with the same parameter name then that component will be replaced.
     * \see parameterComponents()
     * \see setParameterComponents()
     * \see parameterComponent()
     */
    void setParameterComponent( const QgsProcessingModelParameter &component );

    /**
     * Returns the parameter component with matching \a name. If no parameter component exists with
     * this name a new component will be added to the model and returned.
     * \see parameterComponents()
     * \see setParameterComponents()
     * \see setParameterComponent()
     */
    QgsProcessingModelParameter &parameterComponent( const QString &name );

    /**
     * Returns an ordered list of parameters for the model.
     *
     * \see setParameterOrder()
     * \since QGIS 3.14
     */
    QList< QgsProcessingModelParameter > orderedParameters() const;

    /**
     * Sets the \a order for showing parameters for the model.
     *
     * The \a order list should consist of parameter names corresponding to existing
     * model parameterComponents().
     *
     * \see orderedParameters()
     * \since QGIS 3.14
     */
    void setParameterOrder( const QStringList &order );

    /**
     * Returns an ordered list of outputs for the model.
     *
     * \see setOutputOrder()
     * \since QGIS 3.32
     */
    QList< QgsProcessingModelOutput > orderedOutputs() const;

    /**
     * Sets the \a order for sorting outputs for the model.
     *
     * The \a order list should consist of "output child algorithm id:output name" formatted strings corresponding to existing
     * model outputs.
     *
     * \see orderedOutputs()
     * \since QGIS 3.32
     */
    void setOutputOrder( const QStringList &order );

    /**
     * Returns the destination layer tree group name for outputs created by the model.
     *
     * \see setOutputGroup()
     * \since QGIS 3.32
     */
    QString outputGroup() const;

    /**
     * Sets the destination layer tree \a group name for outputs created by the model.
     *
     * \see outputGroup()
     * \since QGIS 3.32
     */
    void setOutputGroup( const QString &group );

    /**
     * Updates the model's parameter definitions to include all relevant destination
     * parameters as required by child algorithm ModelOutputs.
     * Must be called whenever child algorithm ModelOutputs are altered.
     */
    void updateDestinationParameters();

    /**
     * Adds a new group \a box to the model.
     *
     * If an existing group box with the same uuid already exists then its definition will be replaced.
     *
     * \see groupBoxes()
     * \since QGIS 3.14
     */
    void addGroupBox( const QgsProcessingModelGroupBox &groupBox );

    /**
     * Returns a list of the group boxes within the model.
     *
     * \see addGroupBox()
     * \since QGIS 3.14
     */
    QList< QgsProcessingModelGroupBox > groupBoxes() const;

    /**
     * Removes the group box with matching \a uuid from the model.
     *
     * \see addGroupBox()
     * \see groupBoxes()
     * \since QGIS 3.14
     */
    void removeGroupBox( const QString &uuid );

    /**
     * Writes the model to a file, at the specified \a path.
     * \see fromFile()
     */
    bool toFile( const QString &path ) const;

    /**
     * Reads the model from a file, at the specified \a path.
     * \see toFile()
     */
    bool fromFile( const QString &path );

    /**
     * Saves this model to a QVariantMap, wrapped in a QVariant.
     * You can use QgsXmlUtils::writeVariant to save it to an XML document.
     *
     * \see loadVariant()
     * \since QGIS 3.4
     */
    QVariant toVariant() const;

    /**
     * Loads this model from a QVariantMap, wrapped in a QVariant \a variant.
     * You can use QgsXmlUtils::readVariant to load it from an XML document.
     *
     * \see toVariant()
     * \since QGIS 3.4
     */
    bool loadVariant( const QVariant &variant );

    /**
     * Returns the model's help contents (a free-form map of values describing the algorithm's
     * use and metadata).
     * \see setHelpContent()
     */
    QVariantMap &helpContent() { return mHelpContent; }

    /**
     * Returns the model's help contents (a free-form map of values describing the algorithm's
     * use and metadata).
     * \see setHelpContent()
     */
    SIP_SKIP QVariantMap helpContent() const;

    /**
     * Sets the model's help \a contents (a free-form map of values describing the algorithm's
     * use and metadata).
     * \see helpContent()
     */
    void setHelpContent( const QVariantMap &contents );

    /**
     * Returns the source file path for the model, if available.
     * \see setSourceFilePath()
     */
    QString sourceFilePath() const;

    /**
     * Sets the source file \a path for the model, if available.
     * \see sourceFilePath()
     */
    void setSourceFilePath( const QString &path );

    /**
     * Returns TRUE if the model name matches the current model sourceFilePath().
     *
     * Specifically, this method will return true if the complete base name of sourceFilePath()
     * is identical (case-insensitive) to the model name.
     *
     * \since QGIS 3.24
     */
    bool modelNameMatchesFilePath() const;

    /**
     * Attempts to convert the model to executable Python code, and returns the generated lines of code.
     *
     * The \a outputType argument dictates the desired script type.
     *
     * The \a indentSize arguments specifies the size of indented lines.
     */
    QStringList asPythonCode( QgsProcessing::PythonOutputType outputType, int indentSize ) const;

    /**
     * Returns a list of possible sources which can be used for the parameter \a param for a child \a childId
     * algorithm in the model
     *
     * The source compatibilibity is deducted from the parameter type
     *
     * \since QGIS 3.44
     */
    QList< QgsProcessingModelChildParameterSource > availableSourcesForChild( const QString &childId, const QgsProcessingParameterDefinition *param ) const;

    /**
     * Returns a list of possible sources which can be used for the parameters for a child
     * algorithm in the model. Returned sources are those which match either one of the
     * specified \a parameterTypes (see QgsProcessingParameterDefinition::type() ) or
     * one of the specified \a outputTypes (see QgsProcessingOutputDefinition::type() ).
     * If specified, an optional list of \a dataTypes can be used to filter the returned
     * sources to those with compatible data types for the parameter/outputs.
     *
     * Use it when you need fine grained support for the requested compatible sources
     */
    QList< QgsProcessingModelChildParameterSource > availableSourcesForChild(
      const QString &childId, const QStringList &parameterTypes = QStringList(), const QStringList &outputTypes = QStringList(), const QList< int > &dataTypes = QList< int >()
    ) const;

    /**
     * \brief Definition of a expression context variable available during model execution.
     * \ingroup core
     */
    class CORE_EXPORT VariableDefinition
    {
      public:
        /**
         * Constructor for a new VariableDefinition with the specified \a value and original
         * parameter \a source, and \a description.
         */
        VariableDefinition(
          const QVariant &value = QVariant(),
          const QgsProcessingModelChildParameterSource &source = QgsProcessingModelChildParameterSource::fromStaticValue( QVariant() ),
          const QString &description = QString()
        )
          : value( value )
          , source( source )
          , description( description )
        {}

        //! Value of variable
        QVariant value;

        //! Original source of variable's value
        QgsProcessingModelChildParameterSource source;

        //! Translated description of variable
        QString description;
    };

    /**
     * Returns a map of variable name to variable definition for expression context variables which are available
     * for use by child algorithm during model execution.
     *
     * The child algorithm \a childId and processing \a context
     * are manadatory. If \a modelParameters and \a results are not specified, then only the variable names and sources
     * will be returned, but all variable values will be null. This can be used to determine in advance which variables
     * will be available for a specific child algorithm, e.g. for use in expression builder widgets.
     *
     * In order to calculate the actual variable value, the input model \a modelParameters and already executed child
     * algorithm \a results must be passed.
     * \see createExpressionContextScopeForChildAlgorithm()
     */
    QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition > variablesForChildAlgorithm(
      const QString &childId, QgsProcessingContext *context = nullptr, const QVariantMap &modelParameters = QVariantMap(), const QVariantMap &results = QVariantMap()
    ) const;

    /**
     * Creates a new expression context scope for a child algorithm within the model.
     * \see variablesForChildAlgorithm()
     */
    QgsExpressionContextScope *createExpressionContextScopeForChildAlgorithm(
      const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters = QVariantMap(), const QVariantMap &results = QVariantMap()
    ) const SIP_FACTORY;

    /**
     * Returns the map of custom expression context variables defined for this model.
     *
     * These variables are added to the model's expression context scope, allowing for preset
     * "constant" expression variables to be utilized within the model.
     *
     * \see setVariables()
     * \since QGIS 3.8
     */
    QVariantMap variables() const;

    /**
     * Sets the map of custom expression context \a variables for this model.
     *
     * These variables are added to the model's expression context scope, allowing for preset
     * "constant" expression variables to be utilized within the model.
     *
     * \see variables()
     * \since QGIS 3.8
     */
    void setVariables( const QVariantMap &variables );

    /**
     * Returns the parameter values to use as default values when running this model through the
     * designer dialog.
     *
     * This usually corresponds to the last set of parameter values used when the model was
     * run through the designer.
     *
     * \see setDesignerParameterValues()
     *
     * \since QGIS 3.14
     */
    QVariantMap designerParameterValues() const;

    /**
     * Sets the parameter \a values to use as default values when running this model through the
     * designer dialog.
     *
     * This usually corresponds to the last set of parameter values used when the model was
     * run through the designer.
     *
     * \see designerParameterValues()
     *
     * \since QGIS 3.14
     */
    void setDesignerParameterValues( const QVariantMap &values ) { mDesignerParameterValues = values; }

    /**
     * Given a child algorithm ID and output name, attempts to match it to a parameter definition from the overall model.
     *
     * \since QGIS 3.26
     */
    const QgsProcessingParameterDefinition *modelParameterFromChildIdAndOutputName( const QString &childId, const QString &childOutputName ) const;

    /**
     * Makes a name "safe", by replacing any non-alphanumeric characters with underscores.
     *
     * If \a capitalize is TRUE then the string will be converted to a camel case string.
     *
     * \since QGIS 3.26
     */
    static QString safeName( const QString &name, bool capitalize = false );

#ifndef SIP_RUN

    //! Internal model versions
    enum class InternalVersion
    {
      Version1, //!< Created in < 3.26
      Version2, //!< Created in >= 3.26
    };
    Q_ENUM( InternalVersion )

#endif

  protected:
    QgsProcessingAlgorithm *createInstance() const override SIP_FACTORY;

    QVariantMap processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override SIP_THROW( QgsProcessingException );

  private:
    InternalVersion mInternalVersion = InternalVersion::Version2;

    QString mModelName;
    QString mModelGroup;
    QString mModelGroupId;

    QMap< QString, QgsProcessingModelChildAlgorithm > mChildAlgorithms;

    //! Map of parameter name to model parameter component
    QMap< QString, QgsProcessingModelParameter > mParameterComponents;

    QVariantMap mHelpContent;

    //! Model source file
    QString mSourceFile;

    QVariantMap mResults;

    QVariantMap mVariables;

    QVariantMap mDesignerParameterValues;

    QMap< QString, QgsProcessingModelGroupBox > mGroupBoxes;

    QStringList mParameterOrder;
    QStringList mOutputOrder;
    QString mOutputGroup;

    void dependsOnChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends ) const;
    void dependentChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends, const QString &branch ) const;

    QVariantMap parametersForChildAlgorithm(
      const QgsProcessingModelChildAlgorithm &child,
      const QVariantMap &modelParameters,
      const QVariantMap &results,
      const QgsExpressionContext &expressionContext,
      QString &error,
      const QgsProcessingContext *context = nullptr
    ) const;

    /**
     * Returns TRUE if an output from a child algorithm is required elsewhere in
     * the model.
     */
    bool childOutputIsRequired( const QString &childId, const QString &outputName ) const;

    /**
     * Checks whether the output vector type given by \a outputType is compatible
     * with the list of acceptable data types specified by \a acceptableDataTypes.
     * Returns TRUE if vector output is compatible.
     *
     * \note This method is intended to be "permissive" rather than "restrictive".
     * I.e. we only reject outputs which we know can NEVER be acceptable, but
     * if there's doubt then we default to returning TRUE.
     */
    static bool vectorOutputIsCompatibleType( const QList<int> &acceptableDataTypes, Qgis::ProcessingSourceType outputType );

    /**
     * Tries to reattach all child algorithms to their linked algorithms.
     */
    void reattachAlgorithms() const;

    friend class TestQgsProcessingModelAlgorithm;
};

///@endcond

#endif // QGSPROCESSINGMODELALGORITHM_H
