#include "Base/Axis/MakeScale.h"
#include "Base/Math/Numeric.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 "Tests/GTestWrapper/google_test.h"
#include "Tests/Unit/GUI/Utils.h"
#include <QTest>
#include <fstream>

namespace {

const QString dir_test_real_model = "TestDatafilesSet";

void checkDataVsData(const Datafield& data1, const Datafield& data2)
{
    EXPECT_EQ(data1.size(), data2.size());
    bool ok = true;
    for (size_t i = 0; i < data1.size(); ++i) {
        if (Numeric::relativeDifference(data1[i], data2[i]) > 1e-13) {
            std::cout << "file!=data at i=" << i << ": " << data1[i] << " vs " << data2[i]
                      << std::endl;
            ok = false;
        }
    }
    EXPECT_TRUE(ok);
}

} // namespace


TEST(DatafilesSet, 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);
    ::checkDataVsData(dataOnDisk1, *item1->dataItem()->c_field());
    ::checkDataVsData(dataOnDisk2, *item2->dataItem()->c_field());

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

    checkDataVsData(dataOnDisk1, *item1->dataItem()->c_field());
    checkDataVsData(dataOnDisk2, *item2->dataItem()->c_field());

    // check that data on disk has changed
    Datafield dataOnDisk3 = IO::readData2D(fname2.toStdString(), IO::Filetype2D::bornagain2D);
    ::checkDataVsData(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 fname4 = "./" + dir + "/realdata_data2new.int";
    EXPECT_TRUE(QFile::exists(fname4));
    Datafield dataOnDisk4 = IO::readData2D(fname4.toStdString(), IO::Filetype2D::bornagain2D);
    ::checkDataVsData(dataOnDisk4, *item2->dataItem()->c_field());

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

TEST(DatafilesSet, 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);

    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);

    // 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);

    // 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);
}
