/*
 * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#ifndef mvdI18nCoreApplication_h
#define mvdI18nCoreApplication_h

//
// Configuration include.
//// Included at first position before any other ones.
#include "ConfigureMonteverdi.h"

#include "OTBMonteverdiCoreExport.h"

/*****************************************************************************/
/* INCLUDE SECTION                                                           */

//
// Qt includes (sorted by alphabetic order)
//// Must be included before system/custom includes.
#include <QtCore>

//
// System includes (sorted by alphabetic order)

//
// OTB includes (sorted by alphabetic order)

//
// Monteverdi includes (sorted by alphabetic order)
#include "mvdAlgorithm.h"
#include "mvdSystemError.h"
#include "mvdTypes.h"

//
// Macros.


/*****************************************************************************/
/* PRE-DECLARATION SECTION                                                   */


//
// Classes pre-declaration.
namespace
{
}

namespace mvd
{
//
// Internal classes pre-declaration.
class AbstractModel;
class DatasetModel;
class VectorImageModel;

/*****************************************************************************/
/* CLASS DEFINITION SECTION                                                  */

/** \class I18nCoreApplication
 *
 * \ingroup OTBMonteverdiCore
 *
 * \brief The MVD2 core-application (which is different from
 * QCoreApplication).
 *
 * I18nCoreApplication do not derive from QCoreApplication to
 * prevent a dread diamong multiple inheritance scheme which could not
 * be resolved using virtual inheritance (because Qt is not designed
 * for).
 *
 * I18nCoreApplication does not derive from QApplication to
 * prevent package .../Common/Core to depend on QtGui package (which
 * would have broken the Common/Core-Common/Gui package layout).
 *
 * The dread diamond multiple inheritance scheme is solved using
 * aggregation.
 *
 * QCoreApplication is passed as argument of I18nCoreApplication
 * constructor. So, is user application must derive QApplication (to,
 * for example, provide sessio management), the specialized instance
 * can be passed as argument of constructor.
 */
class OTBMonteverdiCore_EXPORT I18nCoreApplication : public QObject
{

  /*-[ QOBJECT SECTION ]-----------------------------------------------------*/

  Q_OBJECT;

  /*-[ PUBLIC SECTION ]------------------------------------------------------*/

  //
  // Public constants & types.
public:
  enum SettingsKey
  {
    SETTINGS_KEY_GEOID_PATH,
    SETTINGS_KEY_GEOID_PATH_ACTIVE,
    SETTINGS_KEY_OVERVIEWS_ENABLED,
    SETTINGS_KEY_OVERVIEWS_SIZE,
    SETTINGS_KEY_RESOLUTION,
    SETTINGS_KEY_RESULTS_DIR,
    SETTINGS_KEY_SRTM_DIR,
    SETTINGS_KEY_SRTM_DIR_ACTIVE,
    SETTINGS_KEY_TILE_SIZE,
    //
    SETTINGS_KEY_COUNT
  };

  //
  // Public methods.
public:
  /**
   * \brief Constructor.
   *
   * \param qtApp The parent Qt application of this MVD2 application.
   */
  I18nCoreApplication(QCoreApplication* qtApp);

  /** \brief Destructor. */
  ~I18nCoreApplication() override;

  /**
   */
  void Initialize();

  //
  // APPLICATION SINGLETON.
  //

  /**
   * \brief Get the singleton instance of application as a
   * I18Application pointer.
   *
   * \return The singleton instance of I18nCoreApplication.
   */
  inline static I18nCoreApplication* Instance();

  /**
   * \brief Get the singleton constant instance of application as a
   * I18Application pointer.
   *
   * \return The singleton constant instance of I18nCoreApplication.
   */
  inline static const I18nCoreApplication* ConstInstance();

  //
  // APPLICATION MODEL.
  //

  /**
   * \brief Access the AbstractModel of the MVC which is managed by the
   * application.
   *
   * \return The managed AbstractModel.
   */
  inline const AbstractModel* GetModel() const;

  /**
  * \brief Access the AbstractModel of the MVC which is managed by the
  * application.
  *
  * \return The managed AbstractModel.
   */
  inline AbstractModel* GetModel();

  /**
   * \brief Access the AbstractModel of the MVC which is managed by the
   * application and cast it into the given dynamic type.
   *
   * \return The managed TModel typename instance.
   */
  template <typename TModel>
  inline const TModel* GetModel() const;

  /**
  * \brief Access the AbstractModel of the MVC which is managed by the
  * application and cast it into the given dynamic type.
  *
  * \return The managed TModel typename instance.
   */
  template <typename TModel>
  inline TModel* GetModel();

  //
  // APPLICATION CACHE-DIRECTORY.
  //


  /**
   * \brief setup elevation management
   *
   */
  bool ElevationSetup();

  //
  // APPLICATION SETTINGS.
  //

  /**
   */
  // TODO: Move method into ApplicationSettings class.
  inline bool HasSettingsKey(SettingsKey) const;

  /**
   */
  // TODO: Move method into ApplicationSettings class.
  inline void StoreSettingsKey(SettingsKey, const QVariant& value);

  /**
   */
  // TODO: Move method into Application class.
  inline QVariant RetrieveSettingsKey(SettingsKey) const;

  /**
   */
  // TODO: Move method into ApplicationSettings class.
  inline bool HasSettingsKey(const QString& key) const;

  /**
   */
  // TODO: Move method into ApplicationSettings class.
  inline void StoreSettingsKey(const QString& key, const QVariant& value);

  /**
   */
  // TODO: Move method into Application class.
  inline QVariant RetrieveSettingsKey(const QString& key) const;

  //
  // STATIC methods.
  //

  /**
   */
  static bool IsResultsDirValid(const QString& path);
  /**
   */
  static bool IsDirValid(const QString& path);

  /**
   * \brief Make directory tree in given path.
   *
   * Throws mvd::SystemError exception if sub-directory tree could not
   * have been created.
   *
   * \param path Location where to create sub-directory tree.
   * \param tree Sub-directory tree path. E.g.: 'level_1/level_2'.
   * \param dir resulting directory, if non-NULL.
   *
   * \return true if sub-directory tree is newly created or false if
   * it is already existing.
   */
  static bool MakeDirTree(const QString& path, const QString& tree, QDir* dir = NULL);

  /**
   * \brief Construct a consistent Dataset path-name.
   *
   * \param path Resulting path where the Dataset will be stored.
   * \param name Resulting name of the given Dataset.
   * \param imageFilename The image filename used to compute the path
   * and name of the Dataset.
   *
   * \return The MD5 hash-code generated from to full absolute
   * pathname of image file.
   */
  static QString DatasetPathName(QString& name, const QString& imageFilename);

  /**
   */
  static VectorImageModel* LoadImageModel(const QString& filename, int width, int height, QObject* p = NULL);

  /**
   */
  static void DeleteDatasetModel(const QString& path, const QString& hash);

  //
  // NON-STATIC methods.
  //

  /**
   * \brief Get the results directory (where output from
   * OTBApplicaitons are stored).
   *
   * \return Return the cache directory.
   */
  inline const QDir& GetResultsDir() const;

  /**
   */
  // TODO: Remove method when Viewer/Application is updated to reuse factorized code of I18nCoreApplication.
  inline QDir& GetResultsDir();

  //
  // Public attributes.
public:
  /**
   * Name of the cache directory
   */
  static const char* DEFAULT_CACHE_DIR_NAME;

  /**
   * Name of the result cache directory
   */
  static const char* DEFAULT_CACHE_RESULT_DIR_NAME;

  /**
   */
  static const char* DATASET_EXT;

  /*-[ PUBLIC SLOTS SECTION ]-----------------------------------------------**/
public slots:
  /**
   * \brief Accessor used to changed model which is managed by the
   * Application.
   *
   * This method emits the AboutToChangeModel() and ModelChanged()
   * signals respectively, before and after, the model is changed.
   *
   * \param model The newly managed model.
   */
  // Method could be inline but it's better not new/delete in inline
  // methods (heap and memory-alignment contexts).
  void SetModel(AbstractModel* model);

  /*-[ SIGNALS SECTION ]-----------------------------------------------------*/

  //
  // SIGNALS.
signals:
  /**
   * \brief Signal emitted when the AbstractModel of the MVC is about
   * to be changed.
   *
   * This signal could be slotted in order to disconnect previously
   * managed model.
   *
   * \param model The newly managed model (previous model can still
   * be accessed using the GetModel() methods).
   */
  void AboutToChangeModel(const AbstractModel* model);

  /**
   * \brief Signal emitted when the AbstractModel of the MVC has been
   * changed.
   *
   * This signal coulb be slotted in order to connect newly managed
   * model.
   *
   * \param model The newly selected model.
   */
  void ModelChanged(AbstractModel* model);

  /*-[ PROTECTED SECTION ]---------------------------------------------------*/

  //
  // Protected methods.
protected:
  /**
   * \brief Get the singleton instance of application (safe) casted
   * into the TApplication type.
   *
   * \return The singleton instance of application (safely) casted
   * into TApplication type.
   */
  template <typename TApplication>
  inline static TApplication* Instance();

  /**
   * \brief Get the constant singleton instance of application (safe)
   * casted  into the TApplication type.
   *
   * \return The constant singleton instance of application (safely)
   * casted into TApplication type.
   */
  template <typename TApplication>
  inline static const TApplication* ConstInstance();

  /**
   */
  void InitializeCore(const QString& appName, const QString& appVersion, const QString& orgName, const QString& orgDomain);


  //
  // Protected attributes.
protected:
  /*-[ PRIVATE SECTION ]-----------------------------------------------------*/

  //
  // Private methods.
private:
  //
  // Static methods.
  //

  /**
   * \brief Handles Qt messages thrown by calls to qDebug(),
   * qWarning(), qCritical(), qFatal().
   *
   * This is a callback method registered into Qt message handling
   * system.
   *
   * \param type Type of caught message.
   * \param message Content of caught message.
   */
  static void HandleQtMessage(QtMsgType type, const QMessageLogContext&, const QString& message);
  /* Old function was
  static void HandleQtMessage( QtMsgType type, const char * message );
  Qstring has a constructor QString(const char *str) but I have no idea
  of the perf impact */

  /**
   */
  virtual void virtual_InitializeCore() = 0;

  /**
   */
  void InitializeLocale();

  /**
   */
  void InitializeSettings();

  /**
   */
  void LoadAndInstallTranslator(const QString& filename, const QString& directory = QString(), const QString& searchDelimiters = QString(),
                                const QString& suffix = QString());

  //
  // Private attributes.
private:
  /**
   */
  static const char* SETTINGS_KEYS[SETTINGS_KEY_COUNT];

  /**
   * \brief I18nCoreApplication singleton instance.
   */
  static I18nCoreApplication* m_Instance;

  /**
  * \brief Directory where all result files from OTB applications are
  * stored
  */
  QDir m_ResultsDir;

  /**
   * \brief Application settings
   */
  QSettings* m_Settings;

  /**
   * \brief AbstractModel of the Model-View-Controller design pattern
   * which is managed by the application.
   */
  AbstractModel* m_Model;

  /**
   */
  bool m_IsRunningFromBuildDir;

  /*-[ PRIVATE SLOTS SECTION ]-----------------------------------------------*/

  //
  // SLOTS
private slots:
};

} // end namespace 'mvd'

/*****************************************************************************/
/* INLINE SECTION                                                            */

namespace mvd
{

/*****************************************************************************/
I18nCoreApplication* I18nCoreApplication::Instance()
{
  return I18nCoreApplication::m_Instance;
}

/*****************************************************************************/
const I18nCoreApplication* I18nCoreApplication::ConstInstance()
{
  return I18nCoreApplication::m_Instance;
}

/*****************************************************************************/
template <typename T>
inline T* I18nCoreApplication::Instance()
{
  return qobject_cast<T*>(I18nCoreApplication::Instance());
}

/*****************************************************************************/
template <typename T>
inline const T* I18nCoreApplication::ConstInstance()
{
  return qobject_cast<const T*>(I18nCoreApplication::ConstInstance());
}

/*****************************************************************************/
inline const AbstractModel* I18nCoreApplication::GetModel() const
{
  return m_Model;
}

/*****************************************************************************/
inline AbstractModel* I18nCoreApplication::GetModel()
{
  return m_Model;
}

/*****************************************************************************/
template <typename TModel>
inline const TModel* I18nCoreApplication::GetModel() const
{
  return qobject_cast<const TModel*>(m_Model);
}

/*****************************************************************************/
template <typename TModel>
inline TModel* I18nCoreApplication::GetModel()
{
  return qobject_cast<TModel*>(m_Model);
}

/*****************************************************************************/
const QDir& I18nCoreApplication::GetResultsDir() const
{
  return m_ResultsDir;
}

/*****************************************************************************/
QDir& I18nCoreApplication::GetResultsDir()
{
  return m_ResultsDir;
}

/*****************************************************************************/
inline bool I18nCoreApplication::HasSettingsKey(SettingsKey key) const
{
  return HasSettingsKey(I18nCoreApplication::SETTINGS_KEYS[key]);
}

/*****************************************************************************/
inline void I18nCoreApplication::StoreSettingsKey(SettingsKey key, const QVariant& value)
{
  StoreSettingsKey(I18nCoreApplication::SETTINGS_KEYS[key], value);
}

/*****************************************************************************/
inline QVariant I18nCoreApplication::RetrieveSettingsKey(SettingsKey key) const
{
  return RetrieveSettingsKey(I18nCoreApplication::SETTINGS_KEYS[key]);
}

/*****************************************************************************/
inline bool I18nCoreApplication::HasSettingsKey(const QString& key) const
{
  assert(m_Settings != NULL);

  return m_Settings->contains(key);
}

/*****************************************************************************/
inline void I18nCoreApplication::StoreSettingsKey(const QString& key, const QVariant& value)
{
  assert(m_Settings != NULL);

  // qDebug() << this << "::StoreSettingsKey(" << key << ", " << value << ")";

  m_Settings->setValue(key, value);
}

/*****************************************************************************/
inline QVariant I18nCoreApplication::RetrieveSettingsKey(const QString& key) const
{
  assert(m_Settings != NULL);

  return m_Settings->value(key);
}

} // end namespace 'mvd'

#endif // I18nCoreApplication_h
