//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Device/RealItem.h
//! @brief     Defines class RealItem
//!
//! @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_DEVICE_REALITEM_H
#define BORNAGAIN_GUI_MODEL_DEVICE_REALITEM_H

#include <QObject>

class Datafield;
class DataItem;
class InstrumentItem;
class IntensityDataItem;
class MaskContainerItem;
class MessageService;
class SpecularDataItem;
class QXmlStreamReader;
class QXmlStreamWriter;

//! Provides access to experimental data, for display and fitting.
//! Owns an IDataLoader.

class RealItem : public QObject {
    Q_OBJECT

    friend class TestView;

public:
    RealItem();
    ~RealItem();

    //! The name which is presented to the user
    QString realItemName() const;
    void setRealItemName(const QString& name);

    QString presentationType() const;
    void setPresentationType(const QString& type);

    // data

    DataItem* dataItem() const;
    IntensityDataItem* intensityDataItem() const;
    SpecularDataItem* specularDataItem() const;

    void initItemWithRank(int rank);

    bool isIntensityData() const;
    bool isSpecularData() const;

    // native data

    bool hasNativeData() const;
    DataItem* nativeDataItem() const;

    DataItem* initNativeData();
    void removeNativeData();

    QString nativeDataUnits() const;
    void setNativeDataUnits(const QString& units);
    bool holdsDimensionalData() const;

    const Datafield* nativeDatafield() const;
    void setNativeDatafield(Datafield* data);

    void setDatafield(Datafield* data);

    //! The name from where the native data was originally imported
    void setNativeFileName(const QString& filename);
    QString nativeFileName() const;

    // instrument

    QString instrumentId() const;
    void setInstrumentId(const QString& id);
    void linkToInstrument(const InstrumentItem* instrument);
    void unlinkFromInstrument();

    //! Returns true, if rotation will affect linked instrument or mask presence.
    bool rotationAffectsSetup() const;
    void rotateData();

    void updateDataFileName();

    // write/read

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

    void writeDataFiles(const QString& projectDir) const;
    QString readDataFiles(const QString& projectDir, MessageService* messageService);

    // other

    //! Make a copy instance.
    void copyTo(RealItem* const realdata_new) const;

    //! Returns the shape of underlying data item
    std::vector<int> shape() const;

    //! Returns mask container item
    MaskContainerItem* maskContainerItem();

signals:
    void importContentsProcessed();

private:
    //! This does not set the link to the instrument! Use linkToInstrument() if you want
    //! to link to this instrument.
    void updateToInstrument(const InstrumentItem* instrument);

private:
    QString m_nativeFileName;
    QString m_instrumentId;
    QString m_name;
    QString m_nativeDataUnits = "nbins";
    QString m_presentationType;

    // DataItem can be `IntensityDataItem` and `SpecularDataItem` (default `IntensityDataItem`).
    /*
      "Native_data" is introduced and used in specular mode to keep the original axis
      of the dataset. It also stores a copy of original intensity values inside datafield,
      but these values are always the same for "data" and "native_data".

      At the moment of loading file to RealItem "data" and "native_data" are completely equal.
      But after linking RealItem to instrument and changing the current units the axis of "data"
      is changed. When we unlink RealItem from instrument, we restore the original axis from
      "native_data", because now there is no other way to transform axis without ICoordSystem
      coming from instrument.
    */
    std::unique_ptr<DataItem> m_dataItem;
    std::unique_ptr<DataItem> m_nativeDataItem;
};

#endif // BORNAGAIN_GUI_MODEL_DEVICE_REALITEM_H
