#ifndef __qSlicerIOManager_h
#define __qSlicerIOManager_h

// Qt includes
#include <QList>
#include <QString>
#include <QUrl>

// CTK includes
#include <ctkVTKObject.h>

// SlicerQ includes
#include "qSlicerApplication.h"
#include "qSlicerCoreIOManager.h"
#include "qSlicerFileDialog.h"

#include "qSlicerBaseQTGUIExport.h"

/// Qt declarations
class QDragEnterEvent;
class QDropEvent;
class QWidget;

class qSlicerIOManagerPrivate;

class Q_SLICER_BASE_QTGUI_EXPORT qSlicerIOManager : public qSlicerCoreIOManager
{
  Q_OBJECT;
  QVTK_OBJECT;

public:
  typedef qSlicerCoreIOManager Superclass;
  qSlicerIOManager(QObject* parent = nullptr);
  ~qSlicerIOManager() override;

  /// Search for the most appropriate dialog based on the action and fileType,
  /// and open it. Once the user select the file(s), the action (read or write)
  /// is done. Note that to write a node, the "nodeID" property must be passed.
  /// If no dialog is registered for a given fileType (e.g.
  /// QString("SceneFile")), a default dialog (qSlicerStandardFileDialog) is
  /// used.
  /// If reading files (action == qSlicerFileDialog::Read) and if loadedNodes
  /// is not null, the loadedNodes collection is being populated with the
  /// loaded nodes.
  /// Returns true on success, false otherwise.
  Q_INVOKABLE bool openDialog(qSlicerIO::IOFileType fileType,
                              qSlicerFileDialog::IOAction action,
                              qSlicerIO::IOProperties ioProperties = qSlicerIO::IOProperties(),
                              vtkCollection* loadedNodes = nullptr);

  void addHistory(const QString& path);
  const QStringList& history() const;

  void setFavorites(const QList<QUrl>& urls);
  const QList<QUrl>& favorites() const;

  /// Takes ownership. Any previously set dialog corresponding to the same
  /// fileType (only 1 dialog per filetype) is overridden.
  void registerDialog(qSlicerFileDialog* dialog);

  /// Return True if a custom file dialog was registered.
  ///
  /// \sa registerDialog()
  /// \sa qSlicerScriptedLoadableModule::registerFileDialog()
  /// \sa qSlicerScriptedFileDialog
  Q_INVOKABLE bool isDialogRegistered(qSlicerIO::IOFileType fileType, qSlicerFileDialog::IOAction action) const;

  /// Displays a progress dialog if it takes too long to load
  /// There is no way to know in advance how long the loading will take, so the
  /// progress dialog listens to the scene and increment the progress anytime
  /// a node is added.
  Q_INVOKABLE bool loadNodes(const qSlicerIO::IOFileType& fileType,
                             const qSlicerIO::IOProperties& parameters,
                             vtkCollection* loadedNodes = nullptr,
                             vtkMRMLMessageCollection* userMessages = nullptr) override;
  /// If you have a list of nodes to load, it's best to use this function
  /// in order to have a unique progress dialog instead of multiple ones.
  /// It internally calls loadNodes() for each file.
  bool loadNodes(const QList<qSlicerIO::IOProperties>& files, vtkCollection* loadedNodes = nullptr, vtkMRMLMessageCollection* userMessages = nullptr) override;

  /// Helper function to display result of loadNodes.
  /// If success is set false then an error popup is displayed.
  /// If success is set to true then a popup is only displayed if error or warning messages are logged.
  /// If a popup is displayed then all the user-displayable messages are displayed in a collapsed "Details" section.
  /// The dialog is not displayed if the application is launched with testing mode enabled.
  Q_INVOKABLE static void showLoadNodesResultDialog(bool success, vtkMRMLMessageCollection* userMessages);

  /// dragEnterEvents can be forwarded to the IOManager, if a registered dialog
  /// supports it, the event is accepted, otherwise ignored.
  /// \sa dropEvent()
  void dragEnterEvent(QDragEnterEvent* event);

  /// Search, in the list of registered readers, the first dialog that
  /// handles the drop event. If the event is accepted by the dialog (
  /// usually the is also used to populate the dialog), the manager opens the dialog,
  /// otherwise the next dialog is tested. The order in which dialogs are
  /// being tested is the opposite of the dialogs are registered.
  /// \sa dragEnterEvent()
  void dropEvent(QDropEvent* event);

public slots:

  void openScreenshotDialog();
  void openSceneViewsDialog();
  bool openLoadSceneDialog();
  bool openAddSceneDialog();
  inline bool openAddDataDialog();
  inline bool openAddDataDialog(QString fileName);
  inline bool openAddVolumeDialog();
  inline bool openAddVolumesDialog();
  inline bool openAddModelDialog();
  inline bool openAddScalarOverlayDialog();
  inline bool openAddTransformDialog();
  inline bool openAddColorTableDialog();
  inline bool openAddFiducialDialog();
  inline bool openAddMarkupsDialog();
  inline bool openAddFiberBundleDialog();
  inline bool openSaveDataDialog();

protected slots:
  void updateProgressDialog();
  void execDelayedFileDialog();

protected:
  friend class qSlicerFileDialog;
  using qSlicerCoreIOManager::readers;

protected:
  QScopedPointer<qSlicerIOManagerPrivate> d_ptr;

private:
  Q_DECLARE_PRIVATE(qSlicerIOManager);
  Q_DISABLE_COPY(qSlicerIOManager);
};

//------------------------------------------------------------------------------
bool qSlicerIOManager::openAddDataDialog(QString fileName)
{
  qSlicerIO::IOProperties ioProperties;
  ioProperties["fileName"] = fileName;
  return this->openDialog(QString("NoFile"), qSlicerFileDialog::Read, ioProperties);
}

//------------------------------------------------------------------------------
bool qSlicerIOManager::openAddDataDialog()
{
  return this->openDialog(QString("NoFile"), qSlicerFileDialog::Read);
}

//-----------------------------------------------------------------------------
bool qSlicerIOManager::openAddVolumeDialog()
{
  return this->openDialog(QString("VolumeFile"), qSlicerFileDialog::Read);
}

//-----------------------------------------------------------------------------
bool qSlicerIOManager::openAddVolumesDialog()
{
  qSlicerIO::IOProperties ioProperties;
  ioProperties["multipleFiles"] = true;
  return this->openDialog(QString("VolumeFile"), qSlicerFileDialog::Read, ioProperties);
}

//-----------------------------------------------------------------------------
bool qSlicerIOManager::openAddModelDialog()
{
  return this->openDialog(QString("ModelFile"), qSlicerFileDialog::Read);
}

//-----------------------------------------------------------------------------
bool qSlicerIOManager::openAddScalarOverlayDialog()
{
  return this->openDialog(QString("ScalarOverlayFile"), qSlicerFileDialog::Read);
}

//-----------------------------------------------------------------------------
bool qSlicerIOManager::openAddTransformDialog()
{
  return this->openDialog(QString("TransformFile"), qSlicerFileDialog::Read);
}

//-----------------------------------------------------------------------------
bool qSlicerIOManager::openAddColorTableDialog()
{
  return this->openDialog(QString("ColorTableFile"), qSlicerFileDialog::Read);
}

//-----------------------------------------------------------------------------
bool qSlicerIOManager::openAddFiducialDialog()
{
  return this->openDialog(QString("FiducialListFile"), qSlicerFileDialog::Read);
}

//-----------------------------------------------------------------------------
bool qSlicerIOManager::openAddMarkupsDialog()
{
  return this->openDialog(QString("MarkupsFiducials"), qSlicerFileDialog::Read);
}

//-----------------------------------------------------------------------------
bool qSlicerIOManager::openAddFiberBundleDialog()
{
  return this->openDialog(QString("FiberBundleFile"), qSlicerFileDialog::Read);
}

//------------------------------------------------------------------------------
bool qSlicerIOManager::openSaveDataDialog()
{
  return this->openDialog(QString("NoFile"), qSlicerFileDialog::Write);
}

#endif
