/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 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 CAMITKAPPLICATION_H
#define CAMITKAPPLICATION_H

// -- Core stuff
#include "CamiTKAPI.h"
#include "InterfaceLogger.h"

// -- QT stuff
#include <QApplication>
#include <QSettings>
#include <QDir>
#include <QFileInfo>
#include <QStack>
#include <QList>
#include <QTranslator>

class vtkObject;
namespace camitk {
class MainWindow;
class ActionExtension;
class ViewerExtension;
class Action;
class Viewer;
class HistoryItem;
class ComponentExtension;
class Property;
class PropertyObject;
class SplashScreen;

/**
 * @ingroup group_sdk_libraries_core_application
 *
 * @brief
 * The generic/default application.
 *  Once this class is instantiated in the main, everything is setup.
 *  The constructor can take the command line arguments.
 *  It can also be asked not to load the extensions automatically,see Application().
 *
 *  If you do not have a specific MainWindow extension, then the default CamiTK MainWindow is used, see setMainWindow().
 *
 * This class manages all application-level instances, structures and all. This explains the number of _static_ methods
 * in this class.
 *
 * It manages:
 * - the registered/loaded action extensions and all the actions
 * - the registered/loaded component extensions and all the component instances
 * - the registered/loaded viewer extensions and all the viewer instances
 * - the refresh mechanism
 * - the current selection (selected components)
 * - the recently opened documents
 * - the application language/internationalization settings
 * - the opening/closing/saving of components
 * - the main window
 * - the history of applied actions (including saving it as a CamitK SCXML document)
 * - some application level settings
 *
 */
class CAMITK_API Application : public QApplication {
    Q_OBJECT

public:

    /**
     * Initializes the window system and constructs a CamiTK application object with argc command line arguments in argv.
     * The first parameter is the name of the application (used as a identifier in the settings, for example)
     *
     * The second and third parameters comes from the command line (see QApplication API documentation).
     * This constructor inits all the CamiTK context:
     *     - application wide settings
     *     - autoload (or not, depending on the last parameter) of the extension
     *
     * \note that you have to call init before doing anything!
     *
     * @param name the name of the application, it will be used to save specific configuration for example.
     * @param argc the number of command line arguments
     * @param argv the values of the command line arguments
     * @param autoloadExtension if true, all the plugins are loaded
     * @param registerFileExtension if true, the application will prompt the user at first run if
     *        she/he wants to register file formats handled by loaded components with this application
     *        for opening (only valid for windows).
     * @param useSplashScreen if true a splash screen is instantiated to show status message during launch
     *
     * \note registerFileExtension is only valid on Windows platform.
     */
    Application(QString name, int& argc, char** argv, bool autoloadExtension = true, bool registerFileExtension = false, bool useSplashScreen = false);

    /// destructor
    ~Application() override;

    /** @name Application specific
     */
    /// @{

    /// reimplemented from QApplication to catch all exception from external libs used in CEP (e.g. from ITK) and avoid crashes...
    bool notify(QObject*, QEvent*) override;

    /// get the application name
    static QString getName();

    /** Get the Core wide settings.
     *  This is the preferred methods for accessing and writing the settings for your specific needs,
     *  although you can use any kind of settings you like, using this allow you to store all
     *  settings in one place for all Core needs.
     *  This settings are stored in the user scope, using the INI format (i.e. no registers !),
     *  the organisation name is TIMC-IMAG and the application name is equal to Core::version().
     *  Check the QSettings API documentation to know exactly where is the settings file or
     *  call Application::getSettings().fileName()
     *
     *  The recommended method is to use one section for each Core area.
     *  Use the beginGroup("my area")/endGroup() to define specific settings area.
     *
     *  \note for each beginGroup you use, you HAVE TO use an endGroup(), otherwise the
     *  settings state integrity is not guaranteed!
     */
    static QSettings& getSettings();

    /** Overriden from QApplication:
     * Enters the main event loop and waits until exit() is called, then returns the value that was set to exit() (which is 0 if exit() is called via quit()).
     *
     * It is necessary to call this function to start event handling. The main event loop receives events from the window system and
     * dispatches these to the application widgets.
     *
     * Generally, no user interaction can take place before calling exec(). As a special case, modal widgets like QMessageBox can be used before calling exec(),
     * because modal widgets call exec() to start a local event loop.
     *
     * To make your application perform idle processing, i.e., executing a special function whenever there are no pending events, use a QTimer with 0 timeout.
     * More advanced idle processing schemes can be achieved using processEvents().
    */
    static int exec();

    /// @brief ask the user and if ok restart the application for the given reason
    static void restart(QString reason);

    /** Get the last used directory (e.g. the directory of the last opened document)
     */
    static const QDir getLastUsedDirectory();

    /// set (force) the last used directory
    static void setLastUsedDirectory(QDir);

    /** Add a document to the list of recent documents (e.g. when a document was opened)
     *  and update lastUsedDirectory
     */
    static void addRecentDocument(QString);

    /// Get the list of recent documents
    static const QList<QFileInfo> getRecentDocuments();

    /// get the maximal number of recent documents stored
    static const int getMaxRecentDocuments();

    ///@}

    /** @name Component instances management
     */
    ///@{

    /// load the filename and returns the corresponding top level Component (returns nullptr if an error occurs)
    /// @note this method opens the filename and created the associated TOP LEVEL component
    /// If you wish to open a subcomponent (not top level then), prefer directly calling its public constructor.
    /// @param fileName file that contains the component
    /// @param blockRefresh do not refresh the main window after closing the component
    static Component* open(const QString& fileName, bool blockRefresh = false);

    /** load a directory and returns the corresponding Component (returns nullptr if an error occurs)
     * @param dirName the name of the directory to open
     * @param pluginName the name of the plugin to use
     */
    static Component* openDirectory(const QString& dirName, const QString& pluginName);

    /** load a camitk file and its content into the application
     */
    static bool loadWorkspace(const QString& filepath);

    /** Close a Component: if it has been changed, ask the user for more information, then if everything is ok, delete it.
    *    @param component the Component to close.
    *    @param blockRefresh do not refresh the main window after closing the component
    *    @return true if the closing was made, false if the user cancelled the operation or a saving problem occurs
    */
    static bool close(Component* component, bool blockRefresh = false);

    /** save a component to its file (as given by component->getFileName()).
     *  \note the component's file name has to be set prior to call this method.
     *
     *  This method look for the proper loaded ComponentExtension, and call its save(Component*) method
     */
    static bool save(Component* component);

    /** save the current workspace to a .camitk file.
     *
     *  This method uses PersistenceManager to save the workspace
     */
    static bool saveWorkspace(const QString& filepath);

    /**
     * Get a unique name from the given name in the given componentList.
     * By default, it will check the given name against all current top level component names.
     *
     * For example getUniqueComponentName("Mesh", getTopLevelComponents()) may return "Mesh (3)"
     * if there is already a component name "Mesh" and "Mesh (2)"
     */
    static QString getUniqueComponentName(QString name, const ComponentList& components = getTopLevelComponents());

    /** get the current application wide list of instantiated top-level Components.
      * This is the public method (return a const, the top-level component list is private
      * and cannot be modified externally).
      */
    static const ComponentList& getTopLevelComponents();

    /** get the current application wide list of all Components.
      * This is the public method (return a const, the component list is private
      * and cannot be modified externally).
      */
    static const ComponentList& getAllComponents();

    /// does this Component still exist? (components can be deleted)
    static bool isAlive(Component*);

    /// does this Action still exist? (HotPlugAction can be unloaded)
    static bool isAlive(Action*);

    /// Return true if at least one of the opened components has been modified, false otherwise.
    static bool hasModified();

    ///@}

    ///
    /// @name Selection management
    ///
    ///@{
    /** get the currently selected Components.
      * This is the public method (return a const, the selected component list is private
      * and cannot be modified externally).
      */
    static const ComponentList& getSelectedComponents();

    /** clear all the selection, i.e call setSelected(false) for
      * all the previously selected components and clear the list.
      */
    static void clearSelectedComponents();
    ///@}

    /// @name Action instances management
    ///@{

    /// get a registered action given its name
    static Action* getAction(QString);

    /// get all the actions registered in the application (note: the returned ActionList is guaranteed to be sorted by action name and to contain no duplicates)
    static const ActionList getActions();

    /// get all the actions that can be applied on a given component (note: the returned ActionList is guaranteed to be sorted by action name and to contain no duplicates)
    static ActionList getActions(Component*);

    /// Get all the actions that can be applied on any components of the given list of components (note: the returned ActionList is guaranteed to be sorted by action name and to contain no duplicates)
    static ActionList getActions(ComponentList);

    /// get all the actions that of a given tag (note: the returned ActionList is guaranteed to be sorted by action name and to contain no duplicates)
    static ActionList getActions(ComponentList, QString);

    /** register all actions from the given ActionExtension
     *  @return the number of actions effectively registered (in case an action's name is already registered it won't be a second time)
     */
    static int registerAllActions(ActionExtension*);

    /** unregister all actions from the given ActionExtension
    *  @return the number of actions effectively unregistered
    */
    static int unregisterAllActions(ActionExtension*);

    /// set the currently triggered action
    /// This is used by Action::trigger(..) to refresh all the viewer (the viewers
    /// interested by the fact an action was triggered can then update what they need to)
    static void setTriggeredAction(Action* action);

    /// get the currently triggered action
    static Action* getTriggeredAction();
    ///@}

    /// @name Viewer management
    ///@{

    /// get the pointer to a registered viewer given its name
    static Viewer* getViewer(QString name);

    /// instantiate a new viewer of the given name and given class name (Viewer inheriting class).
    /// \note this method does not register the viewer automatically. Please call registerViewer(..) to add it
    /// to the list of registered viewers.
    static Viewer* getNewViewer(QString name, QString className);

    /// get all the viewers registered in the application (note: the returned ViewerList is guaranteed to be sorted by viewer name and to not contain any duplicate)
    static const ViewerList getViewers();

    /// get the viewers that can manage/display the given component (note: the returned ViewerList is guaranteed to be sorted by viewer name and to contain no duplicates)
    static const ViewerList getViewers(Component*);

    /// register a viewer in the viewer list (therefore allowing it to be refreshed by the main window automatically
    static bool registerViewer(Viewer*);

    /** register all viewers from the given ViewerExtension
    *  @return the number of viewers effectively registered (in case a viewer's name is already registered
    * by another viewer extension, it won't be registered a second time)
    */
    static int registerAllViewers(ViewerExtension*);

    /** unregister all viewers from the given ViewerExtension
    *  @return the number of viewer effectively unregistered
    */
    static int unregisterAllViewers(ViewerExtension*);
    ///@}

    /** @name Main Window management
     */
    ///@{

    /** set the main window.
     * You need to call this method in order to use a customized CamiTK MainWindow instead
     * of the default one. This method has to be called <b>after</b> init().
     *
     * It allows you to set the main window using your application extension instance.
     *
     * \note Application takes ownership of the MainWindow pointer and deletes it at the appropriate time.
     *
     * @param mw The instance of main window you want to use for your application (if nullptr a new instance of the MainWindow class is created)
     * @param redirect Start redirection to the application console
     */
    void setMainWindow(MainWindow* mw, bool redirect = true);

    /// get the main window
    /// \note if there is no MainWindow when this method is first called,
    /// then this method makes sure there is a MainWindow by and force the creation of
    /// a default MainWindow (direct instance of the MainWindow class)
    static MainWindow* getMainWindow();

    /// refresh the main window (this will call the refresh method of all viewers)
    static void refresh();

    /** Set a message to the status bar.
     *  By default there is no timeout (default), i.e. the given message remains displayed until this method is
     *  called again (or the status bar is cleared otherwise).
     * @param msg the message to display in the status bar.
     * @param timeout number of milli-seconds (timeout) before the status bar is cleared (default = 0, i.e. until next call)
     */
    static void showStatusBarMessage(QString msg, int timeout = 0);

    /** Resets the progress bar if it exists.
     *  See example of use for ProgressFunction for more detailed explanation.
     */
    static void resetProgressBar();

    /** set the progress bar value, value should be in [0..100].
     * Attempting to change the current value to one outside the minimum-maximum range has no effect on the current value.
     *
     * Consider using vtkProgressFunction()
     */
    static void setProgressBarValue(int);

    /** Observer function to be called by vtkFilters and to update progress bar
     * Example of use:
     * \code
     * Application::showStatusBarMessage("Applying my vtk filter");
     * Application::resetProgressBar();
     * vtkSmartPointer<vtkCallbackCommand> progressCallback = vtkSmartPointer<vtkCallbackCommand>::New();
     * progressCallback->SetCallback(&Application::vtkProgressFunction);
     * myVtkFilter->AddObserver(vtkCommand::ProgressEvent, progressCallback);
     * Application::resetProgressBar();
     * Application::showStatusBarMessage("");
     * \endcode
     */
    static void vtkProgressFunction(vtkObject* caller, long unsigned int, void*, void*);
    ///@}

    ///@name Actions pipeline history
    ///@{
    /** Add the history item to the application history.
     * The item is added to the history stack of actions used in a pipeline
     * @see removeLastHistoryItem() To pop back an action from the history use
     * @param item the item to add to the history
     */
    static void addHistoryItem(HistoryItem item);

    /** Remove the last pushed actions in the history of the current pipeline.
     * @see addHistoryItem() To push back the item use
     */
    static HistoryItem removeLastHistoryItem();

    /**
     * Save the history as an SCXML file, stored using @see addHistoryItem() method.
     * This file can be interpreted by the camitk actionstatemachine executable
     */
    static void saveHistoryAsSCXML();

    ///@}

    /// @name Application resources management
    ///@{
    /**
    * Returns for the current CamiTK application, the selected language (stored in its .ini configuration file)
    */
    static QString getSelectedLanguage();
    ///@}

    /// @name Property management
    ///@{
    /**
     * Get the property object of the application.
     * Note that every time a property is changed, the Application is notified, update its settings and take the
     * new property values into account.
     * @see eventFilter()
     */
    static PropertyObject* getPropertyObject();
    ///@}


    /// @name Friend class
    /// @{
    /// class Component is a friend class: only the class Component can access to the private members addComponent(..) and removeComponent(..)
    friend class Component;
    friend class PersistenceManager;
    /// @}

private slots:
    void quitting();

private:
    /// name of the CamiTK application (used to differentiate settings between CamiTK applications)
    static QString name;

    /// the main window of the CamiTK application
    static MainWindow* mainWindow;

    /// the splash screen (might not be instantiated)
    static SplashScreen* splashScreen;

    /// argc given from command line
    static int argc;

    /// argv given from command line
    static char** argv;

    static Action* currentAction;

    /// @name Recent document management
    ///@{
    /// list of all the remembered recent documents (last opened is last inserted!)
    static QList<QFileInfo> recentDocuments;

    /// last used directory
    static QDir lastUsedDirectory;

    /// max number of recent document (default 10)
    static int maxRecentDocuments;

    /// Provide internationalization support for text output.
    static QTranslator* translator;
    ///@}

    /// @name Components management
    ///@{
    /** get the current application wide list of instantiated top-level Components.
    * This is the private (intern) method.
    * The top-level component list is updated by the Component class top-level constructor.
    * This method follows the "construct on first use" idiom/design-pattern.
    * It therefore avoids the infamous "static initialization order fiasco",
    * see http://www.parashift.com/c++-faq/ctors.html
    */
    static ComponentList& getTopLevelComponentList();

    /** get the current application wide list of all Components.
      * This is the private (intern) method.
      * This holds all the components at any level (full component list), top-level or under.
      * This method follows the "construct on first use" idiom/design-pattern.
      * It therefore avoids the infamous "static initialization order fiasco",
      * see http://www.parashift.com/c++-faq/ctors.html
      */
    static ComponentList& getAllComponentList();

    /** get the currently selected Components.
      * This is the private (intern) method.
      * the current selection (selected Components can at any level).
      * This method follows the "construct on first use" idiom/design-pattern.
      * It therefore avoids the infamous "static initialization order fiasco",
      * see http://www.parashift.com/c++-faq/ctors.html
      */
    static ComponentList& getSelectedComponentList();

    /** insert/remove one specific Component to the selection (at the end).
     *  The selected component list is a QList, because the selection order is important (QList
     *  is order, QSet is not).
     *  But it does not make any sense to have two times the same Component instance
     *  so if isSelected is true we have to "manually" check that it is not already in the list
     *
     * \note do not call this method from anywhere BUT the Component setSelected method;
     * Component is a friend class.
     *
     * @param component the component to insert/remove from the selection
     * @param isSelected if true then insert if not already in the selection, otherwise remove
     */
    static void setSelected(Component* component, bool isSelected);

    /** register a new component either in the full component list, or in the full list
      * and in the top-level list.
      *
      * \note do not call this method from anywhere BUT the Component class destructor;
      * Component is a friend class.
      */
    static void addComponent(Component*);

    /** unregister a Component.
      * This method remove top-level component and other level component
      *
      * \note do not call this method from anywhere BUT the Component class destructor
      * Component is a friend class.
      */
    static void removeComponent(Component*);

    ///@}

    ///@name Application settings
    ///@{

    /// global settings for CamiTK application
    static QSettings settings;

    /// Apply all the property values to update the model (e.g., use the logLevel to modify the actual log level of the application logger)
    static void applyPropertyValues();

    /// Apply main window specific settings once it is set or instantiated (e.g. stylesheet)
    static void applyMainWindowPropertyValues();

    ///@}

    ///@name Actions pipeline history
    ///@{
    /** get the history of actions stored.
     *
     * This methods returns the singleton stack of actions stored in a pipeline as
     * their are applied during the execution of the application.
     *
     * This is the private (intern) method.
     * This history is updated (push(), pop()) by addHistoryItem() and removeHistoryItem()
     * This method follows the "construct on first use" idiom/design-pattern.
     * It therefore avoids the infamous "static initialization order fiasco",
     * see http://www.parashift.com/c++-faq/ctors.html
     */
    static QStack<HistoryItem>& getHistory();

    /// @}

    /// @name Actions management
    ///@{
    /** As actions are mainly sort/compared/process by name, an internal QMap
     * associates all the contains all the registered actions with their name (key)
     * This is the private (intern) method.
     *
     * The action extension map is updated by loadExtension, unloadExtension and autoloadExtensions.
     *
     * This method follows the "construct on first use" idiom/design-pattern.
     * It therefore avoids the infamous "static initialization order fiasco",
     * see http://www.parashift.com/c++-faq/ctors.html
     */
    static QMap<QString, Action*>& getActionMap();

    /// sort an ActionSet by action's name
    static ActionList sort(ActionSet);
    ///@}

    /// @name viewer management
    ///@{
    /** As viewers are mainly sort/compared/process by name, an internal QMap
     * associates all the registered viewers with their name (key).
     * This is the private (intern) method.
     *
     * This is the private (intern) method.
     *
     * The viewer extension map is updated by loadExtension, unloadExtension and autoloadExtensions.
     *
     * This method follows the "construct on first use" idiom/design-pattern.
     * It therefore avoids the infamous "static initialization order fiasco",
     * see http://www.parashift.com/c++-faq/ctors.html
     */
    static QMap<QString, Viewer*>& getViewerMap();

    /// return the viewer extension that manages the given viewer
    static ViewerExtension* getViewerExtension(Viewer* viewer);

    /// sort an ViewerSet by viewer's name
    static ViewerList sort(ViewerSet);
    ///@}

    /// @name Property management
    ///@{
    /**
     * Create all the application properties and add them to the qApp
     */
    static void createProperties();

    /**
     * A simple QObject that holds the CamiTK level properties of the application.
     *
     * @note
     * Use the accessor getPropertyObject() to access this object
     *
     * @see
     * PropertyObject
     */
    static PropertyObject* propertyObject;
    ///@}

    /// Init the application resources by loading the language associated .qml file
    static void initResources();

protected:
    /**
     * Event filter of this class instance to watch its properties instances.
     * Each time a property has dynamically changed, this method is called.
     */
    bool eventFilter(QObject* object, QEvent* event) override;
};



}

#endif // CAMITKAPPLICATION_H
