/***************************************************************************
  attributeformmodelbase.h - AttributeFormModelBase

 ---------------------
 begin                : 16.8.2016
 copyright            : (C) 2016 by Matthias Kuhn
 email                : matthias@opengis.ch
 ***************************************************************************
 *                                                                         *
 *   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 ATTRIBUTEFORMMODELBASE_H
#define ATTRIBUTEFORMMODELBASE_H

#include "featuremodel.h"

#include <QStack>
#include <QStandardItemModel>
#include <qgsattributeeditorcontainer.h>
#include <qgseditformconfig.h>
#include <qgsexpressioncontext.h>

class AttributeFormModelBase : public QStandardItemModel
{
    Q_OBJECT

  public:
    explicit AttributeFormModelBase( QObject *parent = nullptr );

    QHash<int, QByteArray> roleNames() const override;

    bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override;

    FeatureModel *featureModel() const;
    void setFeatureModel( FeatureModel *featureModel );

    bool hasTabs() const;
    void setHasTabs( bool hasTabs );

    //! \copydoc AttributeFormModel::save
    bool save();

    //! \copydoc AttributeFormModel::create
    bool create();

    //! \copydoc AttributeFormModel::deleteFeature
    bool deleteFeature();

    bool constraintsHardValid() const;

    bool constraintsSoftValid() const;

    //! \copydoc AttributeFormModel::attribute
    QVariant attribute( const QString &name );

    //! \copydoc AttributeFormModel::applyFeatureModel
    void applyFeatureModel();

    //! \copydoc AttributeFormModel::applyParentDefaultValues
    void applyParentDefaultValues();

  signals:
    void featureModelChanged();
    void hasTabsChanged();
    void featureChanged();
    void constraintsHardValidChanged();
    void constraintsSoftValidChanged();

  private:
    struct CodeRequirements
    {
        QSet<QString> referencedColumns;
        bool formScope = false;
    };

    /**
     * Generates a root container for autogenerated layouts, so we can just use the same
     * form logic to deal with them.
     */
    QgsAttributeEditorContainer *generateRootContainer() const;

    QgsAttributeEditorContainer *invisibleRootContainer() const;

    void updateAttributeValue( QStandardItem *item );

    void buildForm( QgsAttributeEditorContainer *container,
                    QStandardItem *parent,
                    const QString &parentVisibilityExpressions,
                    QList<QStandardItem *> &containers,
                    int currentTabIndex = 0,
                    int columnCount = 1 );


    //! Synchronize all items linked to the \a fieldIndex to have the same \a value.
    void synchronizeFieldValue( int fieldIndex, QVariant value );

    //! Update default values refering to the \a fieldIndex.
    void updateDefaultValues( int fieldIndex = -1, QVector<int> updatedFields = QVector<int>() );

    //! Update QML, HTML, and text widget code.
    void updateEditorWidgetCodes( const QString &fieldName );

    //! Check if the given \a code requires update.
    bool codeRequiresUpdate( const QString &fieldName, const QString &code, const QRegularExpression &regEx );

    //! Udate the visibility state of groups as well as constraints of field items
    void updateVisibilityAndConstraints( int fieldIndex = -1 );

    void setConstraintsHardValid( bool constraintsHardValid );

    void setConstraintsSoftValid( bool constraintsSoftValid );

    /**
     * finds the best widget type regarding to the field type or the configured widget setup
     * \param fieldIndex to get the field
     * \returns widget setup containing the best widget type
     */
    QgsEditorWidgetSetup findBest( int fieldIndex );

    //! Resets the attribute form model
    void resetModel();

    //! Sets up a connection to listen to project map theme change
    void onMapThemeCollectionChanged();

    FeatureModel *mFeatureModel = nullptr;
    QPointer<QgsVectorLayer> mLayer;
    std::unique_ptr<QgsAttributeEditorContainer> mTemporaryContainer;
    bool mHasTabs = false;

    typedef QPair<QgsExpression, QStandardItem *> VisibilityExpression;
    QList<VisibilityExpression> mVisibilityExpressions;
    QMap<QStandardItem *, int> mFields;
    QMap<QStandardItem *, QString> mEditorWidgetCodes;
    QMap<QString, CodeRequirements> mEditorWidgetCodesRequirements;

    QgsExpressionContext mExpressionContext;
    bool mConstraintsHardValid = true;
    bool mConstraintsSoftValid = true;
};

#endif // ATTRIBUTEFORMMODELBASE_H
