//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Data/DataItem.h
//! @brief     Declares class DataItem.
//!
//! @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_DATA_DATAITEM_H
#define BORNAGAIN_GUI_MODEL_DATA_DATAITEM_H

#include <QDateTime>
#include <QXmlStreamReader>
#include <mutex>

class AmplitudeAxisItem;
class BasicAxisItem;
class Datafield;
class Frame;

//! Abstract base class for Data2DItem and Data1DItem.
//! Owns one simulated data set of type Datafield.

//! For experimental data, use DatafileItem.

class DataItem : public QObject {
    Q_OBJECT
protected:
    explicit DataItem(QString modelType);

public:
    ~DataItem();

    const QString TYPE{"uninitialized"};

    // Returns datafield, owned by this class
    Datafield* p_field() { return m_datafield.get(); }
    const Datafield* c_field() const { return m_datafield.get(); }

    void setOriginalDatafield(const Datafield& data);
    virtual void setDatafield(const Datafield& data);

    //! Sets the raw data vector from external source.
    //! Checks only the equality of data size; no dimension checks are applied.
    void setRawDataVector(const std::vector<double>& data);

    QString fname() const { return m_fname; }
    void setFileName(const QString& filename);
    QString dataFullPath(const QString& projectDir) const;

    QDateTime lastModified() const { return m_last_modified; }
    void setLastModified(const QDateTime& dtime);

    void loadDatafield(const QString& projectDir, int rank);
    void saveDatafield(const QString& projectDir) const;

    // Number of bins in data
    int xSize() const;
    int ySize() const;

    // Returns min and max range of x-axis as given by data
    virtual double xMin() const = 0;
    virtual double xMax() const = 0;

    // Returns min and max range of y-axis as given by data
    virtual double yMin() const = 0;
    virtual double yMax() const = 0;

    // Lower and upper zoom ranges of x-axis
    double lowerX() const;
    double upperX() const;
    void setXrange(double lower, double upper);

    // Lower and upper zoom ranges of y-axis
    double lowerY() const;
    double upperY() const;
    void setYrange(double lower, double upper);

    // Adjust zoom range of this item to the other items range
    void checkXranges(DataItem* sourceItem) const;
    void checkYranges(DataItem* sourceItem) const;
    void checkXYranges(DataItem* sourceItem) const;

    const BasicAxisItem* axItemX() const;
    BasicAxisItem* axItemX();
    const AmplitudeAxisItem* axItemY() const;
    AmplitudeAxisItem* axItemY();

    // Axes titles
    QString xAxisLabel() const;
    QString yAxisLabel() const;

    virtual size_t rank() const = 0;
    //! Returns dimensions and axesbins of data
    virtual std::vector<int> axdims() const = 0;

    //! Set axes viewport to original data.
    virtual void resetView() = 0;

    virtual void writeTo(QXmlStreamWriter* w) const = 0;
    virtual void readFrom(QXmlStreamReader* r) = 0;

    bool wasModifiedSinceLastSave() const;

signals:
    void datafieldChanged();
    void rawDataVectorChanged(const std::vector<double>& data);
    void fnameChanged(const QString& filename);
    void axesUnitsChanged(DataItem* sender = nullptr);
    void axesUnitsReplotRequested();
    void itemAxesRangeChanged();
    void updateOtherPlots(DataItem* sender = nullptr);

protected:
    std::unique_ptr<Datafield> m_datafield; //!< simulation results
    mutable std::mutex m_update_data_mutex;

    QString m_fname;
    QString m_file_dir;
    std::unique_ptr<BasicAxisItem> m_x_axis;
    std::unique_ptr<AmplitudeAxisItem> m_y_axis;

    QDateTime m_last_modified;
    mutable QDateTime m_last_saved;

    void writeBaseTo(QXmlStreamWriter* w) const;
    void readBaseFrom(QXmlStreamReader* r);
};

#endif // BORNAGAIN_GUI_MODEL_DATA_DATAITEM_H
