#include "Device/Data/Datafield.h"
#include "Device/IO/IOFactory.h"
#include "GUI/Model/Data/DataItem.h"
#include "GUI/Model/Device/InstrumentItems.h"
#include "GUI/Model/Device/RealItem.h"
#include "GUI/Model/Job/JobItem.h"
#include "GUI/Model/Model/JobModel.h"
#include "Tests/GTestWrapper/google_test.h"
#include "Tests/Unit/GUI/Utils.h"
#include <QTest>

TEST(TestJobModel, nonXMLData)
{
    JobModel jobModel;

    // initial state
    EXPECT_EQ(jobModel.dataItems().size(), 0);

    // adding JobItem
    auto* jobItem = jobModel.createJobItem();
    auto* instrumentItem = new GISASInstrumentItem();
    jobItem->copyInstrumentIntoJob(instrumentItem);
    jobItem->createSimulatedDataItem();
    EXPECT_EQ(jobModel.dataItems().size(), 1);

    // adding empty RealItem to JobItem
    auto* realItem = jobItem->createRealItem();
    EXPECT_EQ(jobModel.dataItems().size(), 1);

    // create specular data in RealItem
    realItem->setDatafield(UTest::GUI::createData(0.0, UTest::GUI::DIM::D1).release());
    EXPECT_EQ(jobModel.dataItems().size(), 2);

    // checking data items of JobModel
    EXPECT_EQ(jobModel.dataItems().indexOf(jobItem->simulatedDataItem()), 0);
    EXPECT_EQ(jobModel.dataItems().indexOf(realItem->dataItem()), 1);

    // replacing the data inside RealItem with the data of the same dimensions
    realItem->setDatafield(UTest::GUI::createData(2.0, UTest::GUI::DIM::D1).release());
    EXPECT_EQ(jobModel.dataItems().size(), 2);

    // replacing the data inside RealItem with the data of different dimensions
    auto data = UTest::GUI::createData(3.0, UTest::GUI::DIM::D2);
    EXPECT_THROW(dynamic_cast<RealItem*>(realItem)->setDatafield(data.get()), std::runtime_error);

    EXPECT_EQ(jobModel.dataItems().size(), 2);
}

TEST(TestJobModel, saveNonXMLData)
{
    const QString projectDir("DatafieldIOService");
    UTest::GUI::create_dir(projectDir);

    const double value1(1.0), value2(2.0), value3(3.0);

    JobModel jobModel;

    // adding JobItem with instrument
    auto* jobItem = jobModel.createJobItem();
    auto* instrumentItem = new GISASInstrumentItem();
    jobItem->copyInstrumentIntoJob(instrumentItem);

    // create simulated data
    jobItem->createSimulatedDataItem();
    jobItem->simulatedDataItem()->setDatafield(UTest::GUI::createData(value1).release());

    // create real data
    jobItem->createRealItem();
    jobItem->realItem()->setDatafield(UTest::GUI::createData(value2).release());

    // update names
    jobItem->setJobName("name");
    jobItem->updateDataFileName();

    // do saving in the main thread
    jobItem->simulatedDataItem()->setSaveInBackground(false);
    jobItem->realItem()->dataItem()->setSaveInBackground(false);

    // save first time
    jobModel.writeDataFiles(projectDir);
    QTest::qSleep(10);

    // check existence of data on disk
    QString fname1 = "./" + projectDir + "/jobdata_name_0.int.gz";
    QString fname2 = "./" + projectDir + "/refdata_name_0.int.gz";
    EXPECT_TRUE(QFile::exists(fname1));
    EXPECT_TRUE(QFile::exists(fname2));

    // read data from disk, check it is the same
    std::unique_ptr<Datafield> dataOnDisk1(IO::readData2D(fname1.toStdString()));
    std::unique_ptr<Datafield> dataOnDisk2(IO::readData2D(fname2.toStdString()));
    EXPECT_TRUE(UTest::GUI::isTheSame(*dataOnDisk1, *jobItem->simulatedDataItem()->c_field()));
    EXPECT_TRUE(UTest::GUI::isTheSame(*dataOnDisk2, *jobItem->realItem()->dataItem()->c_field()));

    // modify data and save the project.
    jobItem->simulatedDataItem()->setDatafield(UTest::GUI::createData(value3).release());
    jobModel.writeDataFiles(projectDir);
    QTest::qSleep(10);

    // ensure that the simulated data has been changed
    EXPECT_FALSE(UTest::GUI::isTheSame(*dataOnDisk1, *jobItem->simulatedDataItem()->c_field()));
    EXPECT_TRUE(UTest::GUI::isTheSame(*dataOnDisk2, *jobItem->realItem()->dataItem()->c_field()));

    // check that data on disk has changed
    dataOnDisk1.reset(IO::readData2D(fname1.toStdString()));
    EXPECT_TRUE(UTest::GUI::isTheSame(*dataOnDisk1, *jobItem->simulatedDataItem()->c_field()));

    // rename job and check that file on disk changed the name
    jobItem->setJobName("new_job");
    jobItem->updateDataFileName();
    jobModel.writeDataFiles(projectDir);
    QTest::qSleep(10);

    // check existence of new files on disk
    QString fname1new = "./" + projectDir + "/jobdata_new_job_0.int.gz";
    QString fname2new = "./" + projectDir + "/refdata_new_job_0.int.gz";
    EXPECT_TRUE(QFile::exists(fname1new));
    EXPECT_TRUE(QFile::exists(fname2new));

    // check that file with old name was removed
    EXPECT_FALSE(QFile::exists(fname1));
    EXPECT_FALSE(QFile::exists(fname2));
}
