/***************************************************************************
     qgsvariableeditorwidget.h
     -------------------------
    Date                 : April 2015
    Copyright            : (C) 2015 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 QGSVARIABLEEDITORWIDGET_H
#define QGSVARIABLEEDITORWIDGET_H

#include <memory>

#include "qgis_gui.h"
#include "qgis_sip.h"

#include <QItemDelegate>
#include <QTreeWidget>
#include <QWidget>

class QTableWidget;
class QgsExpressionContextScope;
class QPushButton;
class QgsExpressionContext;
class QgsVariableEditorTree;
class VariableEditorDelegate;

/**
 * \ingroup gui
 * \class QgsVariableEditorWidget
 * \brief A tree based widget for editing expression context scope variables.
 *
 * The widget allows editing variables from a QgsExpressionContextScope, and can optionally also show inherited
 * variables from a QgsExpressionContext.
 */

class GUI_EXPORT QgsVariableEditorWidget : public QWidget
{
    Q_OBJECT

    Q_PROPERTY( QString settingGroup READ settingGroup WRITE setSettingGroup )

  public:
    /**
     * Constructor for QgsVariableEditorWidget.
     * \param parent parent widget
     */
    QgsVariableEditorWidget( QWidget *parent SIP_TRANSFERTHIS = nullptr );

    ~QgsVariableEditorWidget() override;

    /**
     * Overwrites the QgsExpressionContext for the widget. Setting a context
     * allows the widget to show all inherited variables for the context,
     * and highlight any overridden variables within scopes.
     * \param context expression context
     * \see context()
     */
    void setContext( QgsExpressionContext *context );

    /**
     * Returns the current expression context for the widget. QgsVariableEditorWidget widgets
     * are created with an empty context by default.
     * \see setContext()
     */
    QgsExpressionContext *context() const { return mContext.get(); }

    /**
     * Sets the editable scope for the widget. Only variables from the editable scope can
     * be modified by users.
     * \param scopeIndex index of current editable scope. Set to -1 to disable
     * editing and make the widget read-only.
     * \see editableScope()
     */
    void setEditableScopeIndex( int scopeIndex );

    /**
     * Returns the current editable scope for the widget.
     * \returns editable scope, or NULLPTR if no editable scope is set
     * \see setEditableScopeIndex()
     */
    QgsExpressionContextScope *editableScope() const;

    /**
     * Sets the setting group for the widget. QgsVariableEditorWidget widgets with
     * the same setting group will synchronise their settings, e.g., the size
     * of columns in the tree widget.
     * \param group setting group
     * \see settingGroup()
     */
    void setSettingGroup( const QString &group ) { mSettingGroup = group; }

    /**
     * Returns the setting group for the widget. QgsVariableEditorWidget widgets with
     * the same setting group will synchronise their settings, e.g., the size
     * of columns in the tree widget.
     * \returns setting group name
     * \see setSettingGroup()
     */
    QString settingGroup() const { return mSettingGroup; }

    /**
     * Returns a map variables set within the editable scope. Read only variables are not
     * returned. This method can be used to retrieve the variables edited an added by
     * users via the widget.
     */
    QVariantMap variablesInActiveScope() const;

  public slots:

    /**
     * Reloads all scopes from the editor's current context. This method should be called
     * after adding or removing scopes from the attached context.
     * \see context()
     */
    void reloadContext();

  signals:

    /**
     * Emitted when the user has modified a scope using the widget.
     */
    void scopeChanged();

  protected:
    void showEvent( QShowEvent *event ) override;

  private:
    std::unique_ptr<QgsExpressionContext> mContext;
    int mEditableScopeIndex = -1;
    QgsVariableEditorTree *mTreeWidget = nullptr;
    QPushButton *mAddButton = nullptr;
    QPushButton *mRemoveButton = nullptr;
    QString mSettingGroup;
    bool mShown = false;

    QString saveKey() const;

  private slots:

    void mAddButton_clicked();
    void mRemoveButton_clicked();
    void selectionChanged();
};


#ifndef SIP_RUN

/// @cond PRIVATE

/* QgsVariableEditorTree is NOT part of the public QGIS api. It's only
 * public here as Qt meta objects can't be nested classes
 */

class QgsVariableEditorTree : public QTreeWidget
{
    Q_OBJECT

  public:
    enum VariableRoles
    {
      ContextIndex = Qt::UserRole,
      RowBaseColor
    };

    explicit QgsVariableEditorTree( QWidget *parent = nullptr );

    QTreeWidgetItem *indexToItem( const QModelIndex &index ) const { return itemFromIndex( index ); }
    QModelIndex itemToIndex( QTreeWidgetItem *item ) const { return indexFromItem( item ); }
    QString variableNameFromItem( QTreeWidgetItem *item ) const { return item ? item->text( 0 ) : QString(); }
    QString variableNameFromIndex( const QModelIndex &index ) const { return variableNameFromItem( itemFromIndex( index ) ); }
    QgsExpressionContextScope *scopeFromItem( QTreeWidgetItem *item ) const;
    QTreeWidgetItem *itemFromVariable( QgsExpressionContextScope *scope, const QString &name ) const;
    void setEditableScopeIndex( int scopeIndex ) { mEditableScopeIndex = scopeIndex; }
    QgsExpressionContextScope *editableScope();
    void setContext( QgsExpressionContext *context ) { mContext = context; }
    void refreshTree();
    void removeItem( QTreeWidgetItem *item );
    void renameItem( QTreeWidgetItem *item, const QString &name );
    void resetTree();
    void emitChanged();

  signals:

    void scopeChanged();

  protected:
    void keyPressEvent( QKeyEvent *event ) override;
    void mousePressEvent( QMouseEvent *event ) override;
    void drawRow( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
    QColor rowColor( int index ) const;
    void toggleContextExpanded( QTreeWidgetItem *item );
    void editNext( const QModelIndex &index );

    QModelIndex moveCursor( CursorAction cursorAction, Qt::KeyboardModifiers modifiers ) override;

    QIcon mExpandIcon;

  private:
    VariableEditorDelegate *mEditorDelegate = nullptr;
    int mEditableScopeIndex = -1;
    QgsExpressionContext *mContext = nullptr;
    QMap<QPair<int, QString>, QTreeWidgetItem *> mVariableToItem;
    QMap<int, QTreeWidgetItem *> mScopeToItem;

    void refreshScopeItems( QgsExpressionContextScope *scope, int scopeIndex );
    void refreshScopeVariables( QgsExpressionContextScope *scope, int scopeIndex );
};


class VariableEditorDelegate : public QItemDelegate
{
    Q_OBJECT

  public:
    VariableEditorDelegate( QObject *parent = nullptr, QgsVariableEditorTree *tree = nullptr )
      : QItemDelegate( parent )
      , mParentTree( tree )
    {}

    QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
    void updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
    QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
    void setModelData( QWidget *widget, QAbstractItemModel *model, const QModelIndex &index ) const override;
    void setEditorData( QWidget *, const QModelIndex & ) const override {}

  private:
    QgsVariableEditorTree *mParentTree = nullptr;
};

/// @endcond

#endif

#endif //QGSVARIABLEEDITORWIDGET_H
