/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2024 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/

#ifndef ACTION_H
#define ACTION_H

// -- Core stuff
#include "CamiTKAPI.h"
#include "InterfacePersistence.h"
#include <Application.h>
#include <Component.h>

#include <QSet>
#include <QWidget>
#include <QString>
#include <QAction>


namespace camitk {

class ActionExtension;
class Property;
class HistoryItem;

/**
 * @ingroup group_sdk_libraries_core_action
 *
 * @brief
 * Action class is an abstract class that enables you to build a action (generally on a component).
 * At least two classes have to be reimplemented to enable the action: ActionExtension + Action
 *
 * This is the list of attributes you need to consider when creating a new action
 * - extension : the ActionExtension class where to register your action;
 * - name: name of the action;
 * - description: tag used to describe the action (also used for tooltip and whatsThis of the corresponding QAction);
 * - componentClassName: the name of the component class on which this action can be applied or "" (default) for generic actions;
 * - family: families of actions allows one to group different actions of the same kind under one name;
 * - tags: tags is a list of words used to define an action. These words can be used to find an action.
 * - widget: a default widget (instance of ActionWidget) is build by default (see below if this is not what you need);
 * - isEmbedded: this boolean defines if the gui widget is embedded in a given parent widget / action widget container (true by default)
 * - icon: the icon used for the visually distinguish the action (used by the corresponding QAction)
 *
 * An Action has a corresponding QAction, see getQAction(), that makes it easy to trigger an action from any Qt GUI (menus, toolbar,
 * push buttons...)
 *
 * If the component class is defined (non empty string), an Action is applied on the currently selected components.
 * If there are no component defined (i.e. you specifies setComponentClassName("")), it means that your action does not
 * need any inputs.
 *
 * Two steps have to be considered when using an action:
 * - Step 1, trigger(): the action is either directly applied (if it does not have any GUI) or it's GUI is shown (using getWidget())
 * - Step 2, apply(): only the action algorithm is applied, i.e., the data are processed
 *
 * The targets can have changed between the time the action is first triggered and the time the action is applied.
 * getWidget() is always called when the targets are updated.
 * Therefore whenever getWidget() is called, you should make sure to update the the action GUI consequently.
 * getTargets() is always updated in trigger() and available.
 *
 * \note
 * trigger() and apply() are public slots. They can be called either directly (classic C++ method invocation) or
 * by connecting them to a QWidget signal.
 *
 * When an action is triggered (e.g., by right clicking in the context menu), the following algorithm applies, see trigger():
 * - 1. Prepare targetComponents (available with getTargets()): only select the compatible components from the selected components
 * - 2. If the action is embedded, get the widget and show it in a parent/container (if parent is not specified, show it in the action viewer)
 * - 3. If the action in not embedded, show it as a dialog
 * - 4. If the action does not have any widget, directly call apply()
 *
 * This means that, if there is a widget, the action algorithm is controlled by the action widget, i.e. apply() is not
 * called by trigger() but should be called by one of the action widget's button.
 *
 * An Action generally is used to wrap an algorithm in CamiTK. If this algorithm has parameters, it is very easy to
 * get these parameters accessible to the user through the ActionWidget.
 * These parameters are in fact defined as Qt dynamic properties.
 *
 * By default an action has a widget, instance of ActionWidget.
 * If ActionWidget does not correspond to what you need, just create a new class inheriting from QWidget, or
 * even better, inheriting from ActionWidget.
 *
 * These are the use cases for using the default behaviour (i.e. an instance of ActionWidget):
 * - your action has some parameters and you need the user to review the default or modify their values before the action is applied,
 * - or your action has no parameters but you still want the user to be applied only if/when the user click on an apply button.
 *
 * ActionWidget should be good enough in most of the cases.
 * The default widget contains a description, a reminder of the current target component names,
 * and an applyable/revertable ObjectController that allows you to edit/modify properties.
 *
 * \note
 * The recommended architecture is for the action widget to call the action's apply method.
 * The widget should only manage user interaction.
 *
 * Here are some notes about the rest of the properties:
 *
 * extensionName is automatically given during the action registration in the ActionExtension.
 *
 * The component property determines on which type of component your action can be applied.
 * Generic actions are action that have an empty component name. Therefore generic actions
 * can be called to generate/synthetize data or initialize resources.
 *
 * You can add any number of tags using the method addTag().
 *
 * If ActionWidget is not what your need, a typical getWidget() method should use the lazy instantiation pattern to instantiate
 * MyVerySpecialActionWidget the first time it is called, and call the MyVerySpecialActionWidget instance's updateTargets() method
 * for any subsequent calls. Something like:
 * \code
 * QWidget *MyAction::getWidget() {
 *    // build or update the widget
 *    if (!myWidget)
 *        myWidget = new MyVerySpecialActionWidget(this);
 *    else
 *        // MyVerySpecialActionWidget should have an update() method
 *        myWidget->update();
 *
 *    return myWidget;
 * }
 * \endcode
 * But of course you can also use any kind of widget you like. ActionWidget is just defining
 * a default widget for an action.
 * If your action does not have any GUI/parameters, just the getWidget() method in order to return nullptr.
 *
 * By default the properties/parameters are not automatically updated when the user change the default widget,
 * they are updated only when the user click on the apply button of the default widget.
 * Use setAutoUpdateProperties(true) to automatically update the action's properties.
 *
 * \note if your action needs to react immediately to a change of value in one of its dynamic properties,
 * you need to the <tt>virtual bool event(QEvent* e)</tt> method. For instance:
 * \code
 * // ---------------------- event ----------------------------
 * bool MyAction::event(QEvent * e) {
 *    if (e->type() == QEvent::DynamicPropertyChange) {
 *        e->accept();
 *        QDynamicPropertyChangeEvent *changeEvent = dynamic_cast<QDynamicPropertyChangeEvent *>(e);*
 *
 *        if (!changeEvent)
 *            return false;
 *
 *        // do something depending of the property that has changed
 *        CAMITK_INFO(tr("[%1] changed to [%2]").arg(changeEvent->propertyName(),property(changeEvent->propertyName()).toString()))
 *
 *        return true;
 *    }
 *
 *    // this is important to continue the process if the event is a different one
 *    return QObject::event(e);
 *}
 * \endcode
 *
 * \note if you use your own MyVerySpecialActionWidget class, make sure it conforms to this behaviour
 * (you can get the desired behaviour by calling getAutoUpdateProperty()
 *
 * By default the action's widget is embedded. If you do not want to embed your action's widget, use setEmbedded(false)
 * in the constructor.
 * When embedded, the parent widget has to be given at triggered time.
 * If there is no parent given for an embedded action, then the action is embedded in the ActionViewer by default.
 *
 * The method apply() must be implemented in your Action.
 *
 * \note at any moment, the selected components on which the action needs to be applied
 * are available by getTargets().
 * targetComponents is filtered so that it only contains compatible components (i.e.,
 * instances of getComponent()).
 *
 * \note About registering your action in the history of the application.
 * Consider registering your action within the application's history once applied. The history of action features
 * a stack of processed action. The application's history of actions allows one to export the saved actions as an XML file for
 * scripting or replaying it. To do so, implement the apply() method in your code, then launch the method applyAndRegister(),
 * which simply wraps the apply() method with the preProcess() and postProcess() methods. You may also connect a SIGNAL to it,
 * as the applyAndRegister() method is a Qt SLOT.
 *
 * \note About creating a pipeline of actions
 * A pipeline of actions is a state machine where each state stands for an action with inputs and output components. The transitions
 * between the states are done by processing the state's action (i.e. by calling the corresponding action's apply() method).
 * Interpreting an pipeline of action is simpler than simply executing the action since the user doesn't need to manually
 * set the inputs and outputs of each action (it is done automatically).
 * If you are willing to write such a pipeline, simply implements the apply() method of each of your action and called the
 * applyInPipeline() (instead of simply apply()). The method applyInPipeline() performs some pre- and post-processing around
 * the method apply(). It has to be used within a pipeline (a chain of actions) where setInputComponents()
 * and getOutputComponents() are needed. preProcessInPipeline() only selects the right components,
 * and postProcess() sets output components and record history.
 *
 *
 * @see RenderingOption For a simple example of an embedded action
 * @see RigidTransform For a simple example of a non-embedded action
 * @see ChangeColor For a simple example of an action with no widget (but with a GUI)
*/

class CAMITK_API Action : public QObject, public InterfacePersistence {
    Q_OBJECT

public:

    /// Default Constructor: the ActionExtension is needed
    Action(ActionExtension*);

    /// Destructor
    ~Action() override;

#ifdef ERROR
#define CAMITK_WINDOWS_SYSTEM_ERROR_SAFEGUARD ERROR
#undef ERROR
#endif
    /// \enum ApplyStatus describes what happened during the application of an algorithm (i.e. results of the apply method)
    enum ApplyStatus {
        SUCCESS,    ///< everything went according to plan
        ERROR,      ///< apply() failed : an error occurred (usually it means that the apply() was interrupted)
        WARNING,    ///< some (partial) error occurred during the application of the algorithm
        ABORTED,    ///< the action was aborted before completion
        TRIGGERED  ///< the action was triggered only, but not applied
    };

#ifdef CAMITK_WINDOWS_SYSTEM_ERROR_SAFEGUARD
#define ERROR CAMITK_WINDOWS_SYSTEM_ERROR_SAFEGUARD
#endif

    ///@return the QString equivalent of the given status
    static QString getStatusAsString(ApplyStatus);



public slots:
    /**
     * This method triggers the action.
     * The parent widget is used if the action is embedded, see class description for more information about the algorithm.
     * This method cannot be redefined in inherited class.
     */
    camitk::Action::ApplyStatus trigger(QWidget* parent = nullptr);

    /**
     * This method is called when the action has to be applied on the target list (get the target lists using getTargets())
     *  It calls the algorithm of your action on the target list of components
     * \note it should never be empty!
     * \note if you wish to call your action and register it within the application history, prefer
     * using the \see Action::applyAndRegister()
     * method
     * @return The status of the apply method.
     */
    virtual camitk::Action::ApplyStatus apply() = 0;

    /**
     * This method is called whenever the action has to be applied on the target list (like the apply()) method
     * AND registered within the application history of actions.
     * \note This is the default behaviour of applying and action.
     * The application's history of actions allows one to export the saved actions as an XML file for scripting or replaying it.
     * @return The status of the apply method.
     **/
    camitk::Action::ApplyStatus applyAndRegister();

public:

    /// @name Pipeline execution of the Action
    ///@{
    /** This method encapsulates the apply() method.
     *  It has to be called within a pipeline (a chain of actions), where a
     *  script or another program calls setInputComponents() and/or getOutputComponents.
     *  It is not needed in the case of graphical interface which trigger the Action's widget
     *  and applies the action on selected components.
     *  When there is no GUI, preProcessInPipeline() and postProcessInPipeline() methods select the right component(s).
     *  As the method apply() is called between preProcessInPipeline() and postProcessInPipeline(), the returned value
     *  is the returned value of apply().
     */
    ApplyStatus applyInPipeline();
    //@}

    /// Specify the input Component(s)
    /// Only applyInPipeline() should be called with this method (maybe apply),
    ///  but not trigger() as its first intruction is to clear the target components list !!!
    void setInputComponents(ComponentList inputs);

    /// Specify the input Components in case of only one Component.
    void setInputComponent(Component* input);

    /// Returns the output Component(s)
    ComponentList getOutputComponents();

    /// Returns the output Components in case of only one Component.
    Component* getOutputComponent();
    //@}

    /// @name Generic action getters
    /// These methods can not be redefined in subclasses.
    /// @{
    /** Get the corresponding QAction.
     *  The corresponding QAction has its triggered() signal connected to the trigger() slot of the action.
     *  It shares the action icon (as the QAction's icon) and name (as the QAction's text).
     *  It also use the descriptions of the action for the tooltip/whatsThis text.
     *
     *  To add a shortcut, simply call getQAction()->setShortcut(..) in the action constructor.
     *  To make this shortcut available for any windows of the application, call getQAction()->setShortcutContext(Qt::ApplicationShortcut);
     *
     *  Override this method if your action can be toggled on/off (for instance an action that modifies the visibility
     *  of a component). This method is called in the Component class to build the component's action menu.
     */
    virtual QAction* getQAction(Component* target = nullptr);

    /// get the name of the action
    QString getName() const {
        return name;
    };

    /// the description of the action
    QString getDescription() const {
        return description;
    };

    ///@cond
    /**
     * TODO CAMITK_API_DEPRECATED. This section list the methods marked as deprecated. They are to be removed in CamiTK 6.0
     * @deprecated
     *
     * DEPRECATED (CamiTK 6.0) -> to be removed
     * Use getComponentClassName()
     */
    /// the name of the component class that can be used by this action
    CAMITK_API_DEPRECATED("Please use getComponentClassName() instead")
    QString getComponent() const {
        return getComponentClassName();
    };
    ///@endcond

    /// the name of the component class that can be used by this action
    QString getComponentClassName() const {
        return componentClassName;
    };

    /// the name of the family in which this action is associated
    QString getFamily() const {
        return family;
    };

    /// the name of the extension in the family in which this action is associated
    QString getExtensionName() const;

    /// the name of the tag called this action
    QStringList getTag() const {
        return tags;
    };

    /// argument use to know if the widget is embedded or not
    bool getEmbedded() const {
        return isEmbedded;
    };
    ///@}

    /// @name Method specific to an action.
    /// @{
    /** This method has to be redefined in your Action only if:
      * - you do not have any widget to control your action (i.e. getWidget() will have to return nullptr),
      * - you do not use the default ActionWidget but another one.
      *
      * In the second case, it is strongly recommended to have a code similar to this:
      * \code
      * QWidget *MyAction::getWidget() {
      *    // build or update the widget
      *    if (!myWidget)
      *        myWidget = new MyVerySpecialActionWidget(this);
      *    else
      *        // MyVerySpecialActionWidget should have an update() method
      *        myWidget->update();
      *
      *    return myWidget;
      * }
      * \endcode
      *
      * The update() method in MyVerySpecialActionWidget is used in case the selection has changed since the
      * last time the widget was shown (a change in the selection often means the targets or the
      * parameter values have changed, the UI should be refreshed as well).
      */
    virtual QWidget* getWidget();

    /// the icon to personalize the action (no icon by default)
    virtual QPixmap getIcon();

    /// the currently selected and valid (regarding the component property) components, for which this action is called
    const ComponentList getTargets() const;

    /// update the target list using the currently selected components
    void updateTargets();
    ///@}

    /// @name Property management
    /// @{
    /// auto update properties
    bool getAutoUpdateProperties() const;

    /// are the properties to be updated every time the user makes a change in the widget (default is false)?
    void setAutoUpdateProperties(bool);

    /** Get a Property given its name
     *  @param name the property name
     *  @return nullptr if the name does not match any property name
     *
     *  @see Property
     */
    Q_INVOKABLE virtual camitk::Property* getProperty(QString name);

    /** Add a new parameter to the action, using the CamiTK property class.
     * If the parameter already exist, it will just change its value.
     *
     * \note
     * The action takes ownership of the Property instance.
     *
     * @return false if the Qt Meta Object property was added by this method (otherwise the property was already defined and true is returned if it was successfully updated)
     */
    virtual bool addParameter(Property*);
    /// @}

    /// @name Frame management
    /// @{
    /**
     * Change the target frame according to the default frame policy regarding the input's one.
     * @param input The component referring to for the computation of the frame.
     * @param target The component on which we apply the frame policy.
     */
    void applyTargetPosition(Component* input, Component* target);

    /**
     * Change the target frame according to a given frame policy regarding the input's one.
     * @param input The component referring to for the computation of the frame.
     * @param target The component on which we apply the frame policy.
     * @param policy The frame policy to use on the target.
     */
    void applyTargetPosition(Component* input, Component* target, Application::TargetPositionningPolicy policy);
    /// @}

    /**
     * * @name InterfacePersistence
     * Customized InterfacePersistence methods to support geometry data (e.g. color)
     */
    ///@{
    /// Convert all data from the object to a QVariant (usually a QVariantMap)
    virtual QVariant toVariant() const override;

    /// Load data from a QVariant to initialize the current object
    virtual void fromVariant(const QVariant&) override;
    ///@}

protected:
    /** @name Generic action attributes setters
     *  These methods can not be redefined in subclasses but have to be used to ensure name/description uniqueness
     *  among CamiTK.
     */
    /// @{
    /// set the name of the action class
    void setName(QString name);

    /// the description of the action
    void setDescription(QString description);

    ///@cond
    /**
     * TODO CAMITK_API_DEPRECATED. This section list the methods marked as deprecated. They are to be removed in CamiTK 6.0
     * @deprecated
     *
     * DEPRECATED (CamiTK 6.0) -> to be removed
     * Use setComponentClassName(QString)
     */
    /// set the name of the component class that can be used by this action
    CAMITK_API_DEPRECATED("Please use setComponentClassName(QString componentClassName) instead")
    void setComponent(QString component);
    ///@endcond

    /// set the name of the component class that can be used by this action
    void setComponentClassName(QString componentClassName);

    /// the name of the family in which this action is associated
    void setFamily(QString family);

    /// add a tag to the tags list of this action
    void addTag(QString tag);

    /// set the embedded property (an action is embedded by default, unless specified otherwise by explicitly calling this method with false)
    void setEmbedded(bool isEmbedded);

    /// set the Pixmap
    void setIcon(QPixmap);

    /// the action widget
    QWidget* actionWidget;
    ///@}

private:
    /// the name of the action
    QString name;

    /// the description of the action
    QString description;

    /// the name of the component class that can be used by this action
    QString componentClassName;

    /// the name of the family in which this action is associated
    QString family;

    /// the name of the tag called this action
    QStringList tags;

    /// is the widget embedded or not
    bool isEmbedded;

    /// the extension in which this action is declared and registered
    ActionExtension* extension;

    /// the Action pixmap icon
    QPixmap icon;

    /// the corresponding QAction
    QAction* qAction;

    /// list of CamiTK property decorating the dynamic properties (action parameters)
    QMap<QString, Property*> parameterMap;

    /**
     *  The list of valid (regarding the component property) components for which this action is called.
     *  This list is private (use getTargets() in subclasses).
     *  This list may by filled
     *   - either by the trigger() method which takes the currently selected and valid components
     *     (the method trigger() then calls the apply method or the apply() method can be called by the action's widget
     *   - or by the setInputComponent(Component *)/setInputComponents(ComponentList) methods, but then the method
     *     applyInPipeline() should be called (and not directly the apply() method).
     */
    ComponentList targetComponents;

    /// Should the properties/parameters of this action be automatically updated when the user change something in the GUI
    bool autoUpdateProperties;

    /// @name Action history registration
    ///@{
    /**
     * This properties and methods helps registering an applied action in the history of the application.
     * The application's history of actions allows one to export the saved actions as an XML file for scripting or replaying it.
     **/

    /**
     * The @class{HistoryItem} associated to this action.
     **/
    HistoryItem* item;

    /** The list of top level selected components before running the action
     *  This list is used to deduce the number of top level components, modified through applying the action
     **/
    ComponentList topLevelSelectedComponents;

    /**
     * Save the number of top level components loaded in memory before applying the action.
     * This allows one to deduce the number of created / deleted components launching the action.
     */
    void preProcess();

    /**
     * Register the action in the history. The history item registered features the input and output components,
     * which are deduced with the preProcess() and postProcess() functions.
     */
    void postProcess();
    ///@}

    /// @name Pipeline execution of the Action
    ///@{
    /**
     * In case of a pipeline application of the Action (i.e. a chain of actions with no gui),
     * the following properties and methods are used:
     */

    /**
     * List of alive component before the application of the action
     * (to be compared with the list after and deduce outputComponents).
     **/
    ComponentList aliveBeforeComponents;

    /**
     * List returned by getOutputComponents()
     **/
    ComponentList outputComponents;


    /**
     * Selects the right component(s) (the one that has been set by setInputComponents() ),
     * so that the apply method uses the right component(s) through getTargets().
     * If setInputComponents where not called, does not select any component.
     */
    void preProcessInPipeline();

    /**
     * Set the right output component list so that the method getOutputComponents() can be called.
     * Also, register the action in the history.
     */
    void postProcessInPipeline();



    ///@}



};

}

// -------------------- declare the interface for QPluginLoader --------------------
Q_DECLARE_INTERFACE(camitk::Action, "TIMC-IMAG.Action/2.1")

#endif // ACTION_H




