#include "Base/Axis/MakeScale.h"

#include "Device/Data/Datafield.h"
#include "Device/IO/IOFactory.h"
#include "GUI/Model/Data/DataItem.h"
#include "GUI/Model/File/DatafileItem.h"
#include "GUI/Model/File/DatafilesSet.h"
#include "GUI/Model/Sim/BackgroundItems.h"
#include "GUI/Model/Sim/InstrumentItems.h"
#include "GUI/Model/Sim/InstrumentsSet.h"
#include "Tests/GTestWrapper/google_test.h"
#include "Tests/Unit/GUI/Utils.h"
#include <QTest>
#include <fstream>

const QString dir_test_real_model = "TestDatafilesSet";

TEST(TestDatafilesSet, saveNonXMLData)
{
    if (!QFile::exists(dir_test_real_model))
        QDir(".").mkdir(dir_test_real_model);

    const QString dir = dir_test_real_model + "/saveNonXMLData";
    UTest::GUI::create_dir(dir);

    DatafilesSet model;
    DatafileItem* item1 = UTest::GUI::createRealData2D("data1", model, 101.);
    DatafileItem* item2 = UTest::GUI::createRealData2D("data2", model, 102.);

    // save first time
    model.writeDatafiles(dir);
    QTest::qSleep(10);

    // check existence of data on disk
    QString fname1 = dir + "/realdata_data1.int";
    QString fname2 = dir + "/realdata_data2.int";
    EXPECT_TRUE(QFile::exists(fname1));
    EXPECT_TRUE(QFile::exists(fname2));

    // read data from disk, checking it is the same
    Datafield dataOnDisk1 = IO::readData2D(fname1.toStdString(), IO::Filetype2D::bornagain2D);
    Datafield dataOnDisk2 = IO::readData2D(fname2.toStdString(), IO::Filetype2D::bornagain2D);
    EXPECT_TRUE(UTest::GUI::isTheSame(dataOnDisk1, *item1->dataItem()->c_field()));
    EXPECT_TRUE(UTest::GUI::isTheSame(dataOnDisk2, *item2->dataItem()->c_field()));

    // save the project
    model.writeDatafiles(dir);
    QTest::qSleep(10);

    EXPECT_TRUE(UTest::GUI::isTheSame(dataOnDisk1, *item1->dataItem()->c_field()));
    EXPECT_TRUE(UTest::GUI::isTheSame(dataOnDisk2, *item2->dataItem()->c_field()));

    // check that data on disk has changed
    Datafield dataOnDisk3 = IO::readData2D(fname2.toStdString(), IO::Filetype2D::bornagain2D);
    EXPECT_TRUE(UTest::GUI::isTheSame(dataOnDisk3, *item2->dataItem()->c_field()));

    // rename RealData and check that file on disk changed the name
    item2->setDatafileItemName("data2new");
    model.writeDatafiles(dir);
    QTest::qSleep(10);

    QString fname2new = "./" + dir + "/realdata_data2new.int";
    EXPECT_TRUE(QFile::exists(fname2new));
    EXPECT_TRUE(UTest::GUI::isTheSame(fname2new, *item2->dataItem()->c_field()));

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

TEST(TestDatafilesSet, saveXMLData)
{
    if (!QFile::exists(dir_test_real_model))
        QDir(".").mkdir(dir_test_real_model);

    const QString dir = dir_test_real_model + "/saveXMLData";
    UTest::GUI::create_dir(dir);

    InstrumentsSet instrument_model;
    DatafilesSet model1;

    Datafield df1 = UTest::GUI::makeData1D(201.);
    Datafield df2 = UTest::GUI::makeData1D(202.);

    // add specular DatafileItems with non-default parameters
    auto* spec1 = new DatafileItem("spec1", df1);
    model1.add_item(spec1);
    auto* t1 = new SpecularInstrumentItem;
    instrument_model.add_item(t1);

    // add second specular DatafileItem
    auto* spec2 = new DatafileItem("spec2", df2);
    model1.add_item(spec2);

    // add 2D DatafileItems with non-default parameters
    auto* intensity1 = new DatafileItem("GISAS", df2);
    auto* intensity2 = new DatafileItem("Offspec", df2);
    model1.add_item(intensity1);
    model1.add_item(intensity2);
    auto* t2 = new Scatter2DInstrumentItem;
    auto* t3 = new OffspecInstrumentItem;
    instrument_model.add_item(t2);
    instrument_model.add_item(t3);

    // set non-default top-level model parameters
    model1.setCurrentIndex(1);

    // save data to project files
    const QString file1 = dir + "/Project_1.ba";
    const QString file2 = dir + "/Project_2.ba";

    // write changed model to disk
    const QString tag = "DatafilesSet";
    UTest::GUI::writeXMLFile<DatafilesSet>(file1, model1, tag);
    EXPECT_TRUE(QFile::exists(file1));

    // read data to the second model
    DatafilesSet model2;
    UTest::GUI::readXMLFile<DatafilesSet>(file1, model2, tag);

    // write the second model back to disk
    UTest::GUI::writeXMLFile<DatafilesSet>(file2, model2, tag);
    EXPECT_TRUE(QFile::exists(file2));

    // compare files byte-by-byte
    std::ifstream f1(file1.toStdString());
    std::ifstream f2(file2.toStdString());

    using it = std::istreambuf_iterator<char>;

    std::vector<char> contents1((it(f1)), it());
    std::vector<char> contents2((it(f2)), it());

    EXPECT_EQ(contents1, contents2);
}
