#include "Sim/Fitting/SimDataPair.h"
#include "Device/Data/Datafield.h"
#include "Tests/GTestWrapper/google_test.h"
#include "Tests/Unit/Sim/FittingTestHelper.h"
#include <numeric>

TEST(SimDataPair, standardPair)
{
    FittingTestHelper helper;

    simulation_builder_t builder = [&](const mumufit::Parameters& pars) {
        return helper.createSimulation(pars);
    };

    const double exp_value(10.0);
    const double dataset_weight(0.5);

    SimulationWrapper sim;
    sim.cSimulationFn = builder;
    SimDataPair obj(sim, *helper.createTestData(exp_value), dataset_weight);

    // default state, no simulation has been called yet
    EXPECT_THROW(obj.uncertainties_array(), std::runtime_error);
    EXPECT_THROW(obj.simulation_array(), std::runtime_error);
    EXPECT_THROW(obj.experimental_array(), std::runtime_error);
    EXPECT_THROW(obj.simulationResult(), std::runtime_error);
    EXPECT_THROW(obj.experimentalData(), std::runtime_error);

    // calling builder once
    mumufit::Parameters params;
    EXPECT_EQ(helper.m_builder_calls, 0u);
    obj.execSimulation(params);
    EXPECT_EQ(helper.m_builder_calls, 1u);

    // checking arrays
    auto array = obj.simulation_array();
    EXPECT_DOUBLE_EQ(std::accumulate(array.begin(), array.end(), 0.), 0.);
    array = obj.experimental_array();

    // calling builder second time
    obj.execSimulation(params);
    EXPECT_EQ(helper.m_builder_calls, 2u);

    // checking arrays
    array = obj.simulation_array();
    EXPECT_DOUBLE_EQ(std::accumulate(array.begin(), array.end(), 0.), 0.);
    array = obj.experimental_array();
}

TEST(SimDataPair, move)
{
    FittingTestHelper helper;

    simulation_builder_t builder = [&](const mumufit::Parameters& pars) {
        return helper.createSimulation(pars);
    };

    const double exp_value(10.0);

    SimulationWrapper sim;
    sim.cSimulationFn = builder;
    SimDataPair obj(sim, *helper.createTestData(exp_value), 0.5);
    // calling builder once
    mumufit::Parameters params;
    EXPECT_EQ(helper.m_builder_calls, 0u);
    obj.execSimulation(params);
    EXPECT_EQ(helper.m_builder_calls, 1u);

    // Making clone.
    SimDataPair moved = std::move(obj);

    // calling clone's builder once
    moved.execSimulation(params);
    EXPECT_EQ(helper.m_builder_calls, 2u);
}
