/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2016 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO)
 *
 * 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"

// -- 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 Action;
class HistoryItem;
class ComponentExtension;
class Property;
class PropertyObject;

/**
 * @ingroup group_sdk_libraries_core_application
 *
 * @brief
 * The generic/default application.
 *  Once this class is intanciated 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()
 */
class CAMITK_API Application : public QApplication {
    Q_OBJECT

public:

    enum TargetPositionningMethods {
        SAME_TRANSFORMATION, // Child has the same frame as its parent (parent frame = world).
        NO_TRANSFORMATION, // Child has no frame and parent frame = world.
        SUBFRAME // Child has no frame but its parent is set as its parent frame.
    };

    Q_ENUMS(TargetPositionningMethods)


    /**
     * 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:
     *  - log file
     *  - 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 he wants to register file formats handled by loaded components
     * with this application for opening. Works only (at the moment) on Windows platform.
     */
    Application(QString name, int & argc, char ** argv, bool autoloadExtension = true, bool registerFileExtension = false);

    /// destructor
    virtual ~Application();

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

    /// 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 recommanded 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();

    /** 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(QFileInfo);

    /// 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 (return NULL 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.
    static Component * open(const QString &);

    /** load a directory and returns the corresponding Component (return NULL 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);

    /** 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.
    *    @return true if the closing was made, false if the user cancelled the operation or a saving problem occurs
    */
    static bool close(Component *component);

    /** 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);

    /// Only the class Component can access to the private members addComponent(..) and removeComponent(..)
    friend class Component;

    /** get the current application wide list of instanciated 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 exists?
    static bool isAlive(Component *);

    /// 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 garanteed 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 garanteed 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 garanteed 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 *);
    ///@}

    /** @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 NULL a new instance of the MainWindow class is created)
     */
    void setMainWindow(MainWindow * mw);

    /// get the main window
    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 eventId, void* clientData, void* callData);
    ///@}

    ///@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 saveHistoryAsSXML();

    ///@}

    /// @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.
     */
    PropertyObject* getPropertyObject();

    /**
     * Get the CamiTK property associated with auto loading last opened component when the application starts.
     */
    Property* getAutoloadLastOpenedComponentsProperty() {
        return autoloadLastOpenedComponentsProperty;
    }

    /**
     * Get the CamiTK property associated with the new component frame policy.
     */
    Property* getDefaultTargetedComponentProperty() {
        return defaultTargetedComponentProperty;
    }
    ///}@


    /// @name Frame management
    ///@{
    /**
    * @brief Get the default target positionning method.
    */
    static TargetPositionningMethods getDefaultTargetPositionningMethod();

    /**
     * @brief Set the default target positionning method.
     */
    static void setDefaultTargetPositionningMethod(TargetPositionningMethods method);
    ///@}

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;

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

    /// argc given from command line
    int argc;

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

    /// @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.
    QTranslator* translator;
    ///@}

    /// @name Components management
    ///@{
    /** get the current application wide list of instanciated 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 Actions pipeline history
    ///@{
    /// get the history of actions stored.
    /// * This method follows the "construct on first use" idiom/design-pattern.
    /** get the singleton stack of actions stored in a pipeline
     * 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
    ///@{
    /** all the registered actions, this is a QMap as actions are mainly sort/compared/process by name (key).
     * This is the private (intern) method.
     * The component 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);
    ///@}

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

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

    /**
     * The CamiTK property for selecting the frame policy.
     */
    Property *defaultTargetedComponentProperty;

    /**
     * The CamiTK property to open the last components the application opened during its last session.
     */
    Property *autoloadLastOpenedComponentsProperty;

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


};



}

Q_DECLARE_METATYPE(camitk::Application::TargetPositionningMethods)

#endif // CAMITKAPPLICATION_H
