//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Tests/Suite/GUI/Check.cpp
//! @brief     Implements function checkSimulation for GUI standard test.
//!
//! @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)
//
//  ************************************************************************************************

#include "BABuild.h"
#include "BATesting.h"
#include "Base/Util/PathUtil.h"
#include "Device/Data/Datafield.h"
#include "Device/IO/DiffUtil.h"
#include "Device/IO/IOFactory.h"
#include "GUI/Model/FromCore/ItemizeSample.h"
#include "GUI/Model/FromCore/ItemizeSimulation.h"
#include "GUI/Model/Sample/SampleItem.h"
#include "GUI/Model/Sim/BackgroundItems.h"
#include "GUI/Model/Sim/InstrumentItems.h"
#include "GUI/Model/Sim/SimulationOptionsItem.h"
#include "GUI/Model/ToCore/SimulationToCore.h"
#include "Sim/Simulation/ISimulation.h"
#include <filesystem>
#include <iostream>

namespace fs = std::filesystem;

//! Run simulation directly (in core) and through GUI model, and compare results.

bool checkSimulation(const std::string& name, ISimulation& sim, double precision, double snr,
                     int allowed_outliers)
{
    std::cout << "Tests/Suite/GUI: " << name << std::endl;

    std::cout << "  Round trip (simulation -> GUI items -> simulation)" << std::endl;
    std::unique_ptr<SampleItem> sampleItem(GUI::FromCore::itemizeSample(*sim.sample()));
    std::unique_ptr<InstrumentItem> instrumentItem(GUI::FromCore::itemizeInstrument(sim));
    std::unique_ptr<SimulationOptionsItem> optionsItem(GUI::FromCore::itemizeOptions(sim));
    std::unique_ptr<ISimulation> sim2(
        GUI::ToCore::itemsToSimulation(sampleItem.get(), instrumentItem.get(), optionsItem.get()));
    const Datafield data2 = sim2->simulate();

    std::cout << "  Direct simulation" << std::endl;
    const Datafield ref_data = sim.simulate();

    std::cout << "  Check difference" << std::endl;
    bool ok = DiffUtil::checkConsistence(data2, ref_data, precision, snr, allowed_outliers);

    if (ok)
        return true;

    // Save simulation, as it differs from reference.
    const auto dir = fs::path(BATesting::TestOutDir) / fs::path("Suite") / fs::path("GUI");
    Base::Path::createDirectories(dir.string());

    const std::map<const std::string, const Datafield&> tosave{{"gui", data2}, {"std", ref_data}};
    for (const auto& [kind, data] : tosave) {
        const std::string out_fname = (dir / fs::path(name + "." + kind + ".int")).string();
        IO::writeDatafield(data, out_fname);
        std::cout << "Saved " << out_fname << "\n";
    }
    std::cout << "Notes:\n"
              << "- to visualize an intensity map, use " << BABuild::srcDir()
              << "/devtools/view/plot_int.py\n"
              << "- to plot a difference image, use " << BABuild::srcDir()
              << "/devtools/view/plot_diff_int.py\n";
    return false;
}
