//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Job/JobItem.h
//! @brief     Defines class JobItem.
//!
//! @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_JOB_JOBITEM_H
#define BORNAGAIN_GUI_MODEL_JOB_JOBITEM_H

#include "GUI/Model/Descriptor/PolyPtr.h"
#include "GUI/Model/Sim/InstrumentCatalog.h"
#include "GUI/Model/Sim/InstrumentItems.h"
#include <QThread>
#include <QXmlStreamReader>

class BatchInfo;
class Data1DItem;
class Data2DItem;
class DataItem;
class Datafield;
class DatafileItem;
class FitSuiteItem;
class JobWorker;
class ParameterContainerItem;
class ParameterTreeItems;
class SampleItem;
class SimulationOptionsItem;

class JobItem : public QObject {
    Q_OBJECT
public:
    JobItem();
    JobItem(const SampleItem* sampleItem, const InstrumentItem* instrumentItem,
            const DatafileItem* dfile_item, const SimulationOptionsItem* optionItem);
    ~JobItem();

    //... job properties

    const BatchInfo* batchInfo() const { return m_batch_info.get(); }
    BatchInfo* batchInfo() { return m_batch_info.get(); }

    void setFailed();

    bool isValidForFitting() const { return bool(m_dfile_item); }

    QString activity() const { return m_activity; }
    void setActivity(const QString& activity) { m_activity = activity; }

    size_t rank() const;

    void createParameterTree();

    //... sample

    SampleItem* sampleItem() { return m_sample_item.get(); }
    const SampleItem* sampleItem() const { return m_sample_item.get(); }

    //... instrument

    InstrumentItem* instrumentItem() const { return m_instrument.certainItem(); }

    //... simulation options

    const SimulationOptionsItem* simulationOptionsItem() const
    {
        return m_simulation_options_item.get();
    }

    //... simulate
    void initWorker();
    void haltWorker();
    JobWorker* worker() { return m_worker.get(); }
    const JobWorker* worker() const { return m_worker.get(); }

    QThread* thread() { return m_thread.get(); }

    //... fitting

    FitSuiteItem* fitSuiteItem() { return m_fit_suite_item.get(); }
    const FitSuiteItem* fitSuiteItem() const { return m_fit_suite_item.get(); }

    ParameterContainerItem* parameterContainerItem() { return m_parameter_container.get(); }

    //... data

    void createSimulatedDataItem();
    Data1DItem* data1DItem();
    Data2DItem* data2DItem();
    DataItem* simulatedDataItem() { return m_simulated_data_item.get(); }

    DataItem* createDiffDataItem();
    DataItem* diffDataItem() { return m_diff_data_item.get(); }

    void copyDatafileItemIntoJob(const DatafileItem* source);
    const DatafileItem* dfileItem() const { return m_dfile_item.get(); }
    void adjustRealDataToJobInstrument();

    void updateFileName();

    //... write/read

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

    void saveDatafields(const QString& projectDir) const;
    void loadDatafields(const QString& projectDir);

signals:
    void jobSelected(JobItem* item);
    void jobFinished(JobItem* job_item);
    void progressIncremented();

private slots:
    void onStartedJob();
    void onItemProgress();
    void onFinishedWork();
    void onFinishedThread();

private:
    void importMasksFromRealData();
    void applyMasksToRealDatafield();

    std::unique_ptr<SimulationOptionsItem> m_simulation_options_item;
    std::unique_ptr<ParameterContainerItem> m_parameter_container;
    std::unique_ptr<SampleItem> m_sample_item;
    PolyPtr<InstrumentItem, InstrumentCatalog> m_instrument;

    std::unique_ptr<BatchInfo> m_batch_info;
    QString m_activity;
    std::unique_ptr<DataItem> m_simulated_data_item;
    std::unique_ptr<DataItem> m_diff_data_item;
    std::unique_ptr<DatafileItem> m_dfile_item;
    std::unique_ptr<FitSuiteItem> m_fit_suite_item;

    std::unique_ptr<JobWorker> m_worker;
    std::unique_ptr<QThread> m_thread;
};

#endif // BORNAGAIN_GUI_MODEL_JOB_JOBITEM_H
