/***************************************************************************
                             qgsmodelgraphicsscene.h
                             -----------------------
    Date                 : March 2020
    Copyright            : (C) 2020 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 QGSMODELGRAPHICSCENE_H
#define QGSMODELGRAPHICSCENE_H

#include "qgis.h"
#include "qgis_gui.h"
#include "qgsprocessingcontext.h"

#include <QGraphicsScene>

class QgsProcessingModelAlgorithm;
class QgsModelComponentGraphicItem;
class QgsProcessingModelParameter;
class QgsProcessingModelChildAlgorithm;
class QgsProcessingModelOutput;
class QgsProcessingModelComponent;
class QgsProcessingModelComment;
class QgsModelChildAlgorithmGraphicItem;
class QgsProcessingModelGroupBox;
class QgsMessageBar;
class QgsModelArrowItem;

///@cond NOT_STABLE

/**
 * \ingroup gui
 * \brief QGraphicsScene subclass representing the model designer.
 * \warning Not stable API
 * \since QGIS 3.14
 */
class GUI_EXPORT QgsModelGraphicsScene : public QGraphicsScene
{
    Q_OBJECT

  public:
    //! Z values for scene items
    enum ZValues
    {
      GroupBox = 0,         //!< A logical group box
      ArrowLink = 1,        //!< An arrow linking model items
      ArrowDecoration = 2,  //!< An arrow decoration (used for display feature count at the moment)
      ModelComponent = 10,  //!< Model components (e.g. algorithms, inputs and outputs)
      MouseHandles = 99,    //!< Mouse handles
      RubberBand = 100,     //!< Rubber band item
      ZSnapIndicator = 101, //!< Z-value for snapping indicator

    };

    //! Flags for controlling how the scene is rendered and scene behavior
    enum Flag SIP_ENUM_BASETYPE( IntFlag )
    {
      FlagHideControls = 1 << 1,     //!< If set, item interactive controls will be hidden
      FlagHideComments = 1 << 2,     //!< If set, comments will be hidden
      FlagHideFeatureCount = 1 << 3, //!< If set, Feature count will be hidden
    };
    Q_DECLARE_FLAGS( Flags, Flag )

    /**
     * Constructor for QgsModelGraphicsScene with the specified \a parent object.
     */
    QgsModelGraphicsScene( QObject *parent SIP_TRANSFERTHIS = nullptr );

    QgsProcessingModelAlgorithm *model();

    void setModel( QgsProcessingModelAlgorithm *model );

    /**
     * Sets the combination of \a flags controlling how the scene is rendered and behaves.
     * \see setFlag()
     * \see flags()
     */
    void setFlags( QgsModelGraphicsScene::Flags flags ) { mFlags = flags; }

    /**
     * Enables or disables a particular \a flag for the scene. Other existing
     * flags are not affected.
     * \see setFlags()
     * \see flags()
     */
    void setFlag( QgsModelGraphicsScene::Flag flag, bool on = true );

    /**
     * Returns the current combination of flags set for the scene.
     * \see setFlags()
     * \see setFlag()
     */
    QgsModelGraphicsScene::Flags flags() const { return mFlags; }

    void mousePressEvent( QGraphicsSceneMouseEvent *event ) override;

    /**
     * Populates the scene by creating items representing the specified \a model.
     */
    void createItems( QgsProcessingModelAlgorithm *model, QgsProcessingContext &context );

    /**
     * Returns list of selected component items.
     */
    QList<QgsModelComponentGraphicItem *> selectedComponentItems();

    /**
     * Returns the topmost component item at a specified \a position.
     */
    QgsModelComponentGraphicItem *componentItemAt( QPointF position ) const;

    /**
     * Returns the graphic item corresponding to the specified group box \a uuid.
     */
    QgsModelComponentGraphicItem *groupBoxItem( const QString &uuid );

    /**
     * Returns the graphic item corresponding to the specified child algorithm
     *
     * \since QGIS 3.44
     */
    QgsModelChildAlgorithmGraphicItem *childAlgorithmItem( const QString &childId );

    /**
     * Returns the QgsModelComponentGraphicItem corresponding to the specified parameter name
     *
     * \since QGIS 3.44
     */
    QgsModelComponentGraphicItem *parameterItem( const QString &name );

    /**
     * Returns the QgsModelComponentGraphicItem corresponding to the specified child algorithm output
     *
     * \since QGIS 4.0
     */
    QgsModelComponentGraphicItem *outputItem( const QString &childId, const QString &childOutputName );

    /**
     * Selects all the components in the scene.
     */
    void selectAll();

    /**
     * Clears any selected items in the scene.
     *
     * Call this method rather than QGraphicsScene::clearSelection, as the latter does
     * not correctly emit signals to allow the scene's model to update.
    */
    void deselectAll();

    /**
     * Clears any selected items and sets \a item as the current selection.
    */
    void setSelectedItem( QgsModelComponentGraphicItem *item );

    /**
     * Sets the \a result of the last run of the model through the designer window.
     */
    void setLastRunResult( const QgsProcessingModelResult &result, QgsProcessingContext &context );

    /**
     * Returns the message bar associated with the scene.
     *
     * \see setMessageBar()
     */
    QgsMessageBar *messageBar() const;

    /**
     * Sets the message \a bar associated with the scene.
     *
     * \see messageBar()
     */
    void setMessageBar( QgsMessageBar *bar );

    /**
     * Shows a warning message, allowing users to click a button to see the full details (\a longMessage).
     */
    void showWarning( const QString &shortMessage, const QString &title, const QString &longMessage, Qgis::MessageLevel level = Qgis::MessageLevel::Warning ) const;

    /**
     * Requests a complete rebuild of a model by emitting the according signal
     *
     * \since QGIS 3.44
     */
    void requestRebuildRequired();

    /**
     * Updates the scene rect based on the bounds of the model.

     * The bounding rectangle of the model is calculated off all components of the model, with an additional margin arounds items.
     *
     * \since QGIS 4.0
     */
    void updateBounds();

  signals:

    /**
     * Emitted when a change in the model requires a full rebuild of the scene.
     */
    void rebuildRequired();

    /**
     * Emitted whenever a component of the model is about to be changed.
     *
    * The \a text argument gives the translated text describing the change about to occur, and the
    * optional \a id can be used to group the associated undo commands.
     */
    void componentAboutToChange( const QString &text, const QString &id = QString() );

    /**
     * Emitted whenever a component of the model is changed.
     */
    void componentChanged();

    /**
     * Emitted whenever the selected item changes.
     * If NULLPTR, no item is selected.
     */
    void selectedItemChanged( QgsModelComponentGraphicItem *selected );

    /**
     * Emitted when the user opts to run selected steps from the model.
     *
     * \since QGIS 3.38
    */
    void runSelected();

    /**
     * Emitted when the user opts to run the part of the model starting from the specified child algorithm.
     *
     * \since QGIS 3.38
     */
    void runFromChild( const QString &childId );

    /**
     * Emitted when the user opts to view previous results from the child algorithm with matching ID.
     *
     * \since QGIS 3.38
     */
    void showChildAlgorithmOutputs( const QString &childId );

    /**
    * Emitted when the user opts to view the previous log from the child algorithm with matching ID.
    *
    * \since QGIS 3.38
    */
    void showChildAlgorithmLog( const QString &childId );

  protected:
    /**
     * Creates a new graphic item for a model parameter.
     */
    virtual QgsModelComponentGraphicItem *createParameterGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelParameter *param ) const SIP_FACTORY;

    /**
     * Creates a new graphic item for a model child algorithm.
     */
    virtual QgsModelChildAlgorithmGraphicItem *createChildAlgGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelChildAlgorithm *child ) const SIP_FACTORY;

    /**
     * Creates a new graphic item for a model output.
     */
    virtual QgsModelComponentGraphicItem *createOutputGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelOutput *output ) const SIP_FACTORY;

    /**
     * Creates a new graphic item for a model comment.
     */
    virtual QgsModelComponentGraphicItem *createCommentGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelComment *comment, QgsModelComponentGraphicItem *parentItem ) const SIP_FACTORY;

    /**
     * Creates a new graphic item for a model group box.
     */
    QgsModelComponentGraphicItem *createGroupBoxGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelGroupBox *box ) const SIP_FACTORY;

  private:
    struct LinkSource
    {
        QgsModelComponentGraphicItem *item = nullptr;
        Qt::Edge edge = Qt::LeftEdge;
        int linkIndex = -1;
    };
    QList<LinkSource> linkSourcesForParameterValue( QgsProcessingModelAlgorithm *model, const QVariant &value, const QString &childId, QgsProcessingContext &context ) const;

    void addCommentItemForComponent( QgsProcessingModelAlgorithm *model, const QgsProcessingModelComponent &component, QgsModelComponentGraphicItem *parentItem );
    void addFeatureCountItemForArrow( QgsModelArrowItem *arrow, const QString &layerId );

    Flags mFlags = Flags();

    QgsProcessingModelAlgorithm *mModel = nullptr;

    QMap<QString, QgsModelComponentGraphicItem *> mParameterItems;
    QMap<QString, QgsModelChildAlgorithmGraphicItem *> mChildAlgorithmItems;
    QMap<QString, QMap<QString, QgsModelComponentGraphicItem *>> mOutputItems;
    QMap<QString, QgsModelComponentGraphicItem *> mGroupBoxItems;
    QgsProcessingModelResult mLastResult;
    QMap<QString, long long> mLastResultCount;

    static constexpr int SCENE_COMPONENT_MARGIN = 500;

    QgsMessageBar *mMessageBar = nullptr;
};

Q_DECLARE_METATYPE( QgsModelGraphicsScene::Flags )

///@endcond

#endif // QGSMODELCOMPONENTGRAPHICITEM_H
