/***************************************************************************
    qgseditorwidgetregistry.h
     --------------------------------------
    Date                 : 24.4.2013
    Copyright            : (C) 2013 Matthias Kuhn
    Email                : matthias at opengis dot 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 QGSEDITORWIDGETREGISTRY_H
#define QGSEDITORWIDGETREGISTRY_H

#include "qgis_gui.h"
#include "qgis_sip.h"
#include "qgsattributeeditorcontext.h"
#include "qgseditorwidgetautoconf.h"
#include "qgseditorwidgetfactory.h"

#include <QMap>
#include <QObject>

class QgsMapLayer;
class QDomNode;
class QgsMapCanvas;
class QgsMessageBar;
class QgsSearchWidgetWrapper;
class QgsEditorWidgetWrapper;
class QgsEditorConfigWidget;
class QgsVectorLayer;


/**
 * \ingroup gui
 * \brief A registry that manages all known edit widget factories.
 *
 * QgsEditorWidgetRegistry is not usually directly created, but rather accessed through
 * QgsGui::editorWidgetRegistry().
 */
class GUI_EXPORT QgsEditorWidgetRegistry : public QObject
{
    Q_OBJECT

  public:
    /**
     * Constructor for QgsEditorWidgetRegistry. QgsEditorWidgetRegistry is not usually directly created, but rather accessed through
     * QgsGui::editorWidgetRegistry().
     */
    QgsEditorWidgetRegistry();

    /**
     * Registers all the default widgets.
     * Only call this once on startup of an application.
     *
     * \param mapCanvas  Specify a map canvas with which the widgets (relation reference) work
     * \param messageBar Specify a message bar on which messages by widgets will be shown while working with the map canvas
     *
     * \note Not required for plugins, the QGIS application does that already
     */
    void initEditors( QgsMapCanvas *mapCanvas = nullptr, QgsMessageBar *messageBar = nullptr );

    /**
     * Destructor
     *
     * Deletes all the registered widgets
     */
    ~QgsEditorWidgetRegistry() override;

    /**
     * Find the best editor widget and its configuration for a given field.
     *
     * \param vl        The vector layer for which this widget will be created
     * \param fieldName The field name on the specified layer for which this widget will be created
     *
     * \returns The id of the widget type to use and its config
     */
    QgsEditorWidgetSetup findBest( const QgsVectorLayer *vl, const QString &fieldName ) const;

    /**
     * Create an attribute editor widget wrapper of a given type for a given field.
     * The editor may be NULLPTR if you want the widget wrapper to create a default widget.
     *
     * \param widgetId  The id of the widget type to create an attribute editor for
     * \param vl        The vector layer for which this widget will be created
     * \param fieldIdx  The field index on the specified layer for which this widget will be created
     * \param config    A configuration which should be used for the widget creation
     * \param editor    An editor widget which will be used instead of an autocreated widget
     * \param parent    The parent which will be used for the created wrapper and the created widget
     * \param context   The editor context (not available in Python bindings)
     *
     * \returns A new widget wrapper
     */
    QgsEditorWidgetWrapper *create(
      const QString &widgetId,
      QgsVectorLayer *vl,
      int fieldIdx,
      const QVariantMap &config,
      QWidget *editor,
      QWidget *parent SIP_TRANSFERTHIS,
      const QgsAttributeEditorContext &context SIP_PYARGREMOVE = QgsAttributeEditorContext()
    ) SIP_FACTORY;

    /**
     * Create an attribute editor widget wrapper of the best type for a given field.
     * The editor may be NULLPTR if you want the widget wrapper to create a default widget.
     *
     * \param vl        The vector layer for which this widget will be created
     * \param fieldIdx  The field index on the specified layer for which this widget will be created
     * \param editor    An editor widget which will be used instead of an autocreated widget
     * \param parent    The parent which will be used for the created wrapper and the created widget
     * \param context   The editor context (not available in Python bindings)
     *
     * \returns A new widget wrapper
     */
    // clang-format off
    QgsEditorWidgetWrapper *create( QgsVectorLayer *vl, int fieldIdx, QWidget *editor, QWidget *parent SIP_TRANSFERTHIS, const QgsAttributeEditorContext &context SIP_PYARGREMOVE = QgsAttributeEditorContext() ) SIP_FACTORY;
    // clang-format on

    QgsSearchWidgetWrapper *createSearchWidget(
      const QString &widgetId,
      QgsVectorLayer *vl,
      int fieldIdx,
      const QVariantMap &config,
      QWidget *parent SIP_TRANSFERTHIS,
      const QgsAttributeEditorContext &context SIP_PYARGREMOVE = QgsAttributeEditorContext()
    ) SIP_FACTORY;

    /**
     * Creates a configuration widget
     *
     * \param widgetId  The id of the widget type to create a configuration widget for
     * \param vl        The vector layer for which this widget will be created
     * \param fieldIdx  The field index on the specified layer for which this widget will be created
     * \param parent    The parent widget for the created widget
     *
     * \returns A new configuration widget
     */
    QgsEditorConfigWidget *createConfigWidget( const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, QWidget *parent SIP_TRANSFERTHIS ) SIP_FACTORY;

    /**
     * Returns the human readable name for a widget type
     *
     * \param widgetId The widget type to get the name for
     *
     * \returns A human readable name
     */
    QString name( const QString &widgetId );

    /**
     * Returns the icon for a widget type
     *
     * \param widgetId The widget type to get the icon for
     *
     * \returns An icon
     *
     * \since QGIS 4.0
     */
    QIcon icon( const QString &widgetId );

    /**
     * Gets the widget's read-only flag
     *
     * \param widgetId The widget type to get the read-only flag for
     *
     * \since QGIS 3.44
     */
    bool isReadOnly( const QString &widgetId );

    /**
     * Gets access to all registered factories
     *
     * \returns All ids and factories
     */
    QMap<QString, QgsEditorWidgetFactory *> factories();

    /**
     * Gets a factory for the given widget type id.
     *
     * \returns A factory or NULLPTR if not existent
     */
    QgsEditorWidgetFactory *factory( const QString &widgetId );

    /**
     * Register a new widget factory with the given id
     *
     * \param widgetId      The id which will be used later to refer to this widget type
     * \param widgetFactory The factory which will create this widget type
     *
     * \returns TRUE, if successful, FALSE, if the widgetId is already in use or widgetFactory is NULLPTR
     */
    bool registerWidget( const QString &widgetId, QgsEditorWidgetFactory *widgetFactory SIP_TRANSFER );

    /**
     * Register a new auto-conf plugin.
     *
     * \param plugin The plugin (ownership is transferred)
     */
    void registerAutoConfPlugin( QgsEditorWidgetAutoConfPlugin *plugin ) { mAutoConf.registerPlugin( plugin ); }

  private:
    QString findSuitableWrapper( QWidget *editor, const QString &defaultWidget );

    QMap<QString, QgsEditorWidgetFactory *> mWidgetFactories;
    QMap<const char *, QPair<int, QString>> mFactoriesByType;
    QgsEditorWidgetAutoConf mAutoConf;
    std::unique_ptr<QgsEditorWidgetFactory> mFallbackWidgetFactory = nullptr;
};

#endif // QGSEDITORWIDGETREGISTRY_H
