//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Project/ProjectDocument.h
//! @brief     Defines class ProjectDocument
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#ifndef BORNAGAIN_GUI_MODEL_PROJECT_PROJECTDOCUMENT_H
#define BORNAGAIN_GUI_MODEL_PROJECT_PROJECTDOCUMENT_H

#include "GUI/Model/Device/MultiInstrumentNotifier.h"
#include "GUI/Model/Model/JobModel.h"
#include "GUI/Model/Model/RealModel.h"
#include "GUI/Model/Project/LinkInstrumentManager.h"
#include "GUI/Model/Sample/SampleModel.h"
#include "GUI/Support/Data/SimulationOptionsItem.h"

class MessageService;
class ProjectDocument;
class QIODevice;

extern std::optional<ProjectDocument*> gProjectDocument;

//! Project document class handles all data related to the opened project
//! (sample, job, project specific settings).
//!
//! It also provides signaling regarding changes in the current document data.
//!
//! e.g. if project file is /home/users/development/Untitled/Untitled.ba
//! projectName()     - 'Untitled'
//! projectDir()      - '/home/users/development/Untitled
//! projectFileName() - '/home/users/development/Untitled/Untitled.ba'
class ProjectDocument : public QObject {
    Q_OBJECT

public:
    enum class ReadResult { ok, warning, error };

    enum Functionality {
        None = 0x0,
        Gisas = 0x1,
        Offspec = 0x2,
        Specular = 0x4,
        Depthprobe = 0x8,
        All = Gisas | Offspec | Specular | Depthprobe
    };
    Q_DECLARE_FLAGS(Functionalities, Functionality)
    Q_FLAG(Functionalities)

    ProjectDocument();

    QString projectName() const;
    void setProjectName(const QString& text);

    QString projectDir() const;
    QString validProjectDir() const;
    void setProjectDir(const QString& text);
    QString userExportDir() const;

    QString projectFullPath() const;
    void setProjectFullPath(const QString& fullPath);

    InstrumentModel* instrumentModel();
    SampleModel* sampleModel();
    RealModel* realModel();
    JobModel* jobModel();
    SimulationOptionsItem* simulationOptionsItem();

    LinkInstrumentManager* linkInstrumentManager();

    //! The edit controller for the instruments in this project document.
    //!
    //! Use this to modify instrument list or instrument data.
    //! Listen to its signals to get notification about any changes.
    //! \sa MultiInstrumentNotifier for more information
    MultiInstrumentNotifier* multiNotifier();

    void saveProjectFileWithData(const QString& projectPullPath);

    ReadResult loadProjectFileWithData(const QString& projectPullPath,
                                       MessageService& messageService);

    bool hasValidNameAndPath();

    bool isModified() const;
    void setModified();
    void clearModified();

    QString documentVersion() const;

    bool singleInstrumentMode() const;
    void setSingleInstrumentMode(bool b);

    bool singleSampleMode() const;
    void setSingleSampleMode(bool b);

    int viewId() const;
    void setViewId(int viewId);

    Functionalities functionalities() const;
    void setFunctionalities(const Functionalities& f);

signals:
    void projectSaved();
    void modifiedStateChanged();
    void functionalitiesChanged();
    void singleInstrumentModeChanged();
    void singleSampleModeChanged();

private:
    void writeProject(QIODevice* device);
    ReadResult readProject(QIODevice* device, MessageService& messageService);

    void onModelChanged();

    QString m_project_dir;
    QString m_project_name;
    bool m_modified;
    QString m_currentVersion;
    std::unique_ptr<LinkInstrumentManager> m_linkManager;
    bool m_singleInstrumentMode;
    bool m_singleSampleMode;
    Functionalities m_functionalities;
    SimulationOptionsItem m_simulationOptionsItem;
    MultiInstrumentNotifier m_instrumentEditController;
    InstrumentModel m_instrumentModel;
    SampleModel m_sampleModel;
    RealModel m_realModel;
    JobModel m_jobModel;
    int m_lastViewActive;
};

Q_DECLARE_OPERATORS_FOR_FLAGS(ProjectDocument::Functionalities)

inline QVariant toVariant(const ProjectDocument::Functionalities& f)
{
    return QVariant(static_cast<ProjectDocument::Functionalities::Int>(f));
}

inline ProjectDocument::Functionalities toFunctionalities(const QVariant& v)
{
    return static_cast<ProjectDocument::Functionalities>(
        v.value<ProjectDocument::Functionalities::Int>());
}

#endif // BORNAGAIN_GUI_MODEL_PROJECT_PROJECTDOCUMENT_H
