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

#ifndef BORNAGAIN_GUI_VIEW_COMMON_DATAACCESSWIDGET_H
#define BORNAGAIN_GUI_VIEW_COMMON_DATAACCESSWIDGET_H

#include "GUI/Model/Device/RealItem.h"
#include "GUI/Model/Job/JobItem.h"
#include <QWidget>

//! The DataAccessWidget class is a base for widgets, working data, stored in JobItem or RealItem.
//! DataAccessWidget provides an access to SpeculatDataItems and IntensityDataItems separately and
//! as a list of items that is required for simultaneous, synchronous work while plotting and
//! changing their properties.

class DataAccessWidget : public QWidget {
    Q_OBJECT
public:
    explicit DataAccessWidget(QWidget* parent = nullptr);
    ~DataAccessWidget() override;

    virtual void setJobOrRealItem(QObject* item);

    virtual void setFourierItem(IntensityDataItem* fftItem);

    virtual QList<QAction*> actionList();

    JobItem* jobItem() const;
    RealItem* realItem() const;
    QObject* jobRealBase() const;

    //... Access to concrete items:

    // Specular

    //! Real data as SpecularDataItem
    SpecularDataItem* realSpecularDataItem() const;

    //! Simulated data as SpecularDataItem
    SpecularDataItem* simuSpecularDataItem() const;

    //! Difference data as SpecularDataItem
    SpecularDataItem* diffSpecularDataItem() const;

    // Intensity

    //! Real as IntensityDataItem
    IntensityDataItem* realIntensityDataItem() const;

    //! Simulated as IntensityDataItem
    IntensityDataItem* simuIntensityDataItem() const;

    //! Difference as IntensityDataItem
    IntensityDataItem* diffIntensityDataItem() const;

    //... Access to lists of items:

    // Specular

    //! Simulated + real specular data as list, non-nullptr items
    QList<SpecularDataItem*> mainSpecularDataItems() const;

    //! Simulated + real + difference specular data as list, non-nullptr items
    QList<SpecularDataItem*> allSpecularDataItems() const;

    //! first element of allSpecularDataItems() with nullptr check
    SpecularDataItem* currentSpecularDataItem() const;

    // Intensity

    //! Simulated + real intensity data as list, non-nullptr items
    QList<IntensityDataItem*> mainIntensityDataItems() const;

    //! Simulated + real + difference intensity data as list, non-nullptr items
    QList<IntensityDataItem*> allIntensityDataItems() const;

    //! first element of allIntensityDataItems() with nullptr check
    IntensityDataItem* currentIntensityDataItem() const;

private:
    template <class T>
    T* realDataItem() const;

    template <class T>
    T* simuDataItem() const;

    template <class T>
    T* diffDataItem() const;

    // simuDataItem() + realItem()
    template <class T>
    QList<T*> mainDataItems() const;

    // diffDataItem() as QList
    template <class T>
    QList<T*> diffDataItems() const;

    // mainDataItems() + diffDataItems()
    template <class T>
    QList<T*> allDataItems() const;

    QObject* m_item;
    IntensityDataItem* m_fftItem;
};

template <class T>
T* DataAccessWidget::realDataItem() const
{
    if (jobItem()) {
        RealItem* real_item_from_job = jobItem()->realItem();
        if (real_item_from_job)
            return dynamic_cast<T*>(real_item_from_job->dataItem());
        else
            return nullptr;
    }
    if (realItem())
        return dynamic_cast<T*>(realItem()->dataItem());

    return nullptr;
}

template <class T>
T* DataAccessWidget::simuDataItem() const
{
    if (!jobItem())
        return nullptr;
    return dynamic_cast<T*>(jobItem()->simulatedDataItem());
}

template <class T>
T* DataAccessWidget::diffDataItem() const
{
    if (!jobItem())
        return nullptr;
    return dynamic_cast<T*>(jobItem()->diffDataItem());
}

template <class T>
QList<T*> DataAccessWidget::mainDataItems() const
{
    QList<T*> output;
    auto* simu = simuDataItem<T>();
    auto* real = realDataItem<T>();
    if (simu)
        output.append(simu);
    if (real)
        output.append(real);
    return output;
}

template <class T>
QList<T*> DataAccessWidget::diffDataItems() const
{
    QList<T*> output;
    auto* diff = diffDataItem<T>();
    if (diff)
        output.append(diff);
    return output;
}

template <class T>
QList<T*> DataAccessWidget::allDataItems() const
{
    return mainDataItems<T>() + diffDataItems<T>();
}

#endif // BORNAGAIN_GUI_VIEW_COMMON_DATAACCESSWIDGET_H
