/* ============================================================
 *
 * This file is a part of digiKam project
 * https://www.digikam.org
 *
 * Date        : 2023-05-15
 * Description : geolocation engine based on Marble.
 *               (c) 2007-2022 Marble Team
 *               https://invent.kde.org/education/marble/-/raw/master/data/credits_authors.html
 *
 * SPDX-FileCopyrightText: 2023-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 * ============================================================ */

#pragma once

// Qt includes

#include <QObject>
#include <QString>
#include <QRegion>

// Local includes

#include "RenderPluginInterface.h"
#include "digikam_export.h"

class QAction;
class QActionGroup;
class QStandardItem;

namespace Marble
{

class MarbleModel;
class RenderPluginModel;

/**
 * @brief The abstract class that creates a renderable item
 *
 * Renderable Plugins can be used to extend Marble's functionality:
 * They allow to draw stuff on top of the map / globe
 *
 */

class DIGIKAM_EXPORT RenderPlugin : public QObject,
    public RenderPluginInterface
{
    Q_OBJECT

    Q_PROPERTY(QString name READ name CONSTANT)
    Q_PROPERTY(QString nameId READ nameId CONSTANT)
    Q_PROPERTY(QString version READ version CONSTANT)
    Q_PROPERTY(QString description READ description CONSTANT)
    Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
    Q_PROPERTY(bool visible READ visible WRITE setVisible NOTIFY visibilityChanged)
    Q_PROPERTY(bool userCheckable READ isUserCheckable WRITE setUserCheckable NOTIFY userCheckableChanged)

public:

    /**
     * @brief A Type of plugin
     */
    enum RenderType
    {
        UnknownRenderType,
        TopLevelRenderType,
        PanelRenderType,
        OnlineRenderType,
        ThemeRenderType
    };

    explicit RenderPlugin(const MarbleModel* marbleModel);
    ~RenderPlugin() override;

    /**
     * @brief String that should be displayed in GUI
     *
     * Using a "&" you can suggest key shortcuts
     *
     * Example: "&Stars"
     *
     * @return string for gui usage
     */
    virtual QString guiString() const = 0;

    /**
     * @brief Creation a new instance of the plugin
     *
     * This method is used to create a new object of the current
     * plugin using the @p marbleModel given.
     *
     * @param marbleModel base model
     * @return new instance of current plugin
     * @note Typically this method is implemented with the help of the MARBLE_PLUGIN() macro.
     */
    virtual RenderPlugin* newInstance(const MarbleModel* marbleModel) const = 0;

    /**
     * @brief Access to the MarbleModel
     *
     * Internal way to access the model of marble.
     * Can be used to interact with the main application
     *
     * @return marble model
     * @see MarbleModel
     */
    const MarbleModel* marbleModel() const;

    /**
     * @brief Getting all actions
     *
     * This method is used by the main window to get all of the actions that this
     * plugin defines. There is no guarantee where the main window will place the
     * actions but it will generally be in a Menu. The returned QList should
     * also contain all of the actions returned by @see toolbarActions().
     *
     * @return a list of grouped actions
     */
    virtual const QList<QActionGroup*>*   actionGroups() const;

    /**
     * @brief Getting all actions which should be placed in the toolbar
     *
     * This method returns a subset of the actions returned by @see actions() which
     * are intended to be placed in a more prominent place such as a toolbar above
     * the Marble Widget. You are not guaranteed that they will be in an actual
     * toolbar but they will be visible and discoverable
     *
     * @return a list of grouped toolbar actions
     */
    virtual const QList<QActionGroup*>*   toolbarActionGroups() const;

    /**
     * @brief is enabled
     *
     * This method indicates enableability of the plugin
     *
     * If plugin is enabled it going to be displayed in Marble Menu
     * as active action which can be @see setUserCheckable
     *
     * @return enableability of the plugin
     * @see setEnabled
     */
    bool    enabled() const;

    /**
     * @brief is visible
     *
     * This method indicates visibility of the plugin
     *
     * If plugin is visible you can see it on the map/globe
     *
     * @return visibility of the plugin
     * @see setVisible
     */
    bool    visible() const;

    /**
     * @brief is user checkable
     *
     * This method indicates user checkability of plugin's
     * action displayed in application menu
     *
     * Can control plugin visibility
     *
     * @warning User can do it only if @see enabled is true
     *
     * @return checkability of the plugin
     * @see setUserCheckable
     */
    bool    isUserCheckable() const;

    /**
     * @brief Settings of the plugin
     *
     * Settings is the map (hash table) of plugin's settings
     * This method is called to determine the current settings of the plugin
     * for serialization, e.g. when closing the application.
     *
     * @return plugin's settings
     * @see setSettings
     */
    virtual QHash<QString, QVariant> settings() const;

    /**
     * @brief Set the settings of the plugin
     *
     * Usually this is called at startup to restore saved settings.
     *
     * @param new plugin's settings
     * @see settings
     */
    virtual void setSettings(const QHash<QString, QVariant>& settings);

    /**
     * @brief Render type of the plugin
     *
     * Function for returning the type of plugin this is for.
     * This affects where in the menu tree the action() is placed.
     *
     * @see RenderType
     * @return: The type of render plugin this is
     */
    virtual RenderType renderType() const;

    RenderState renderState() const override;

    QString runtimeTrace() const override;

public Q_SLOTS:

    /**
     * @brief setting enabled
     *
     * If @p enabled = true, plugin will be enabled
     *
     * If plugin is enabled it will be possible to show/hide it
     * from menu (access from UI)
     *
     * @param enabled plugin's enabled state
     * @see enabled
     */
    void    setEnabled(bool enabled);

    /**
     * @brief setting visible
     *
     * If @p visible = true, plugin will be visible
     *
     * @param visible visibility of the plugin
     * @see visible
     */
    void    setVisible(bool visible);

    /**
     * @brief setting user checkable
     *
     * If @p isUserCheckable = true, user will get an
     * option to control visibility in application menu
     *
     * @param isUserCheckable user checkability of the plugin
     * @see isUserCheckable
     */
    void    setUserCheckable(bool isUserCheckable);

    /**
     * @brief Passes an empty set of settings to the plugin
     *
     * Well behaving plugins restore their settings to default values as a result of calling this method.
     *
     */
    void    restoreDefaultSettings();

    /**
     * @brief Full list of the settings keys
     *
     * This method should be used to get all possible
     * settings' keys for the plugin's settings
     *
     * @return list with the keys of settings
     */
    QStringList settingKeys() const;

    /**
     * @brief Change setting key's values
     * @param key setting key
     * @param value new value
     *
     * This method applies @p value for the @p key
     *
     * @return successfully changed or not
     */
    bool setSetting(const QString& key, const QVariant& value);

    /**
     * @brief Getting setting value from the settings
     * @param key setting's key index
     *
     * This method should be used to get current value of @p key
     * in settings hash table
     *
     * @return setting value
     */
    QVariant setting(const QString& key) const;

    /**
     * @brief Plugin's menu action
     *
     * The action is checkable and controls the visibility of the plugin.
     *
     * @return action, displayed in menu
     */
    QAction* action() const;

Q_SIGNALS:

    /**
     * This signal is Q_EMITted if the visibility is changed with @see setVisible
     */
    void visibilityChanged(bool visible, const QString& nameId);

    /**
     * This signal is Q_EMITted if the enabled property is changed with @see setEnabled
     */
    void enabledChanged(bool enable);

    /**
     * This signal is Q_EMITted if the user checkable property is changed with @see setUserCheckable
     */
    void userCheckableChanged(bool isUserCheckable);

    /**
     * This signal is Q_EMITted if the settings of the RenderPlugin changed.
     */
    void settingsChanged(const QString& nameId);

    /**
     * This signal is Q_EMITted if the actions that the plugin supports change in
     * any way
     */
    void actionGroupsChanged();

    /**
     * This signal is Q_EMITted if an update of the view is needed. If available with the
     * @p dirtyRegion which is the region the view will change in. If dirtyRegion.isEmpty() returns
     * true, the whole viewport has to be repainted.
     */
    void repaintNeeded(const QRegion& dirtyRegion = QRegion());

protected:

    bool eventFilter(QObject*, QEvent*) override;

private:

    friend class RenderPluginModel;

    QStandardItem* item();

    void applyItemState();
    void retrieveItemState();

    Q_DISABLE_COPY(RenderPlugin)

private:

    class Private;
    Private* const d = nullptr;
};

#define MARBLE_PLUGIN(T) public:\
    RenderPlugin* newInstance( const MarbleModel *marbleModel ) const override { return new T( marbleModel ); }

} // namespace Marble
