/***************************************************************************
                         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_core.h"
#include "qgis.h"
#include "qgsprocessingmodelchildalgorithm.h"
#include "qgsprocessingalgorithm.h"
#include "qgsprocessingmodelparameter.h"
#include "qgsprocessingmodelchildparametersource.h"

///@cond NOT_STABLE

/**
 * \class QgsProcessingModelAlgorithm
 * \ingroup core
 * Model based algorithm with processing.
  * \since QGIS 3.0
 */
class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
{
  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

    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;
    Flags flags() 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 );

    /**
     * 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.
     * \see dependsOnChildAlgorithms()
     */
    QSet< QString > dependentChildAlgorithms( const QString &childId ) 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;

    /**
     * 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 );

    /**
     * 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 );

    /**
     * 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();

    /**
     * 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 );

    /**
     * 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 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.
     */
    QList< QgsProcessingModelChildParameterSource > availableSourcesForChild( const QString &childId, const QStringList &parameterTypes = QStringList(),
        const QStringList &outputTypes = QStringList(), const QList< int > &dataTypes = QList< int >() ) const;

    /**
     * Definition of a expression context variable available during model execution.
     * \ingroup core
     * \since QGIS 3.0
     */
    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, 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 );

  protected:

    QgsProcessingAlgorithm *createInstance() const override SIP_FACTORY;

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

  private:

    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;

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

    QVariantMap parametersForChildAlgorithm( const QgsProcessingModelChildAlgorithm &child, const QVariantMap &modelParameters, const QVariantMap &results, const QgsExpressionContext &expressionContext ) 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, QgsProcessing::SourceType outputType );

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

    friend class TestQgsProcessing;
};

///@endcond

#endif // QGSPROCESSINGMODELALGORITHM_H
