#include "Base/Axis/Frame.h"
#include "Base/Axis/FrameUtil.h"
#include "Base/Axis/MakeScale.h"
#include "Base/Axis/Scale.h"
#include "Device/Data/DataUtil.h"
#include "Device/Data/Datafield.h"
#include "Tests/GTestWrapper/google_test.h"

TEST(DataUtilTest, createRearrangedDataSet)
{
    Datafield input_data{
        {newEquiDivision("axis0", 2, 1.0, 2.0), newEquiDivision("axis1", 3, 3.0, 4.0)}};
    input_data.setVector(std::vector<double>{1.0, 2.0, 3.0, 4.0, 5.0, 6.0});

    std::unique_ptr<Datafield> output_data = DataUtil::Data::createRearrangedDataSet(input_data, 5);

    EXPECT_EQ(size_t(3), output_data->axis(0).size());
    EXPECT_EQ(size_t(2), output_data->axis(1).size());

    EXPECT_EQ(input_data[2], (*output_data)[0]);
    EXPECT_EQ(input_data[5], (*output_data)[1]);
    EXPECT_EQ(input_data[1], (*output_data)[2]);
    EXPECT_EQ(input_data[4], (*output_data)[3]);
    EXPECT_EQ(input_data[0], (*output_data)[4]);
    EXPECT_EQ(input_data[3], (*output_data)[5]);

    output_data = DataUtil::Data::createRearrangedDataSet(input_data, -6);

    EXPECT_EQ(size_t(2), output_data->axis(0).size());
    EXPECT_EQ(size_t(3), output_data->axis(1).size());

    EXPECT_EQ(input_data[5], (*output_data)[0]);
    EXPECT_EQ(input_data[4], (*output_data)[1]);
    EXPECT_EQ(input_data[3], (*output_data)[2]);
    EXPECT_EQ(input_data[2], (*output_data)[3]);
    EXPECT_EQ(input_data[1], (*output_data)[4]);
    EXPECT_EQ(input_data[0], (*output_data)[5]);

    output_data = DataUtil::Data::createRearrangedDataSet(input_data, 3);

    EXPECT_EQ(size_t(3), output_data->axis(0).size());
    EXPECT_EQ(size_t(2), output_data->axis(1).size());

    EXPECT_EQ(input_data[3], (*output_data)[0]);
    EXPECT_EQ(input_data[0], (*output_data)[1]);
    EXPECT_EQ(input_data[4], (*output_data)[2]);
    EXPECT_EQ(input_data[1], (*output_data)[3]);
    EXPECT_EQ(input_data[5], (*output_data)[4]);
    EXPECT_EQ(input_data[2], (*output_data)[5]);
}

TEST(DataUtilTest, coordinateToFromBinf)
{
    Scale axis = EquiDivision("axis", 8, -5.0, 3.0);
    EXPECT_EQ(0.5, FrameUtil::coordinateToBinf(-4.5, axis));
    EXPECT_EQ(-4.5, FrameUtil::coordinateFromBinf(0.5, axis));

    EXPECT_EQ(1.0, FrameUtil::coordinateToBinf(-4.0, axis));
    EXPECT_EQ(-4.0, FrameUtil::coordinateFromBinf(1.0, axis));

    EXPECT_EQ(-0.5, FrameUtil::coordinateToBinf(-5.5, axis));
    EXPECT_EQ(-5.5, FrameUtil::coordinateFromBinf(-0.5, axis));

    EXPECT_EQ(8.0, FrameUtil::coordinateToBinf(3.0, axis));
    EXPECT_EQ(3.0, FrameUtil::coordinateFromBinf(8.0, axis));

    EXPECT_EQ(8.5, FrameUtil::coordinateToBinf(3.5, axis));
    EXPECT_EQ(3.5, FrameUtil::coordinateFromBinf(8.5, axis));
}

//! Transformation of coordinates from one Datafield to another using conversion from axes
//! coordinates to bin-fraction-coordinates and then to another axes coordinates.

TEST(DataUtilTest, datafieldToFromBinf)
{
    Frame data1({newEquiDivision("axis0", 8, -5.0, 3.0), newEquiDivision("axis1", 3, 2.0, 5.0)});
    Frame data2(
        {newEquiDivision("axis0", 8, -10.0, 70.0), newEquiDivision("axis1", 3, -10.0, 20.0)});

    double x(-4.5), y(2.5);
    FrameUtil::coordinatesToBinf(x, y, data1);
    FrameUtil::coordinatesFromBinf(x, y, data2);
    EXPECT_DOUBLE_EQ(x, -5.0);
    EXPECT_DOUBLE_EQ(y, -5.0);

    x = 3.1;
    y = 5.1;
    FrameUtil::coordinatesToBinf(x, y, data1);
    FrameUtil::coordinatesFromBinf(x, y, data2);
    EXPECT_DOUBLE_EQ(x, 71.0);
    EXPECT_DOUBLE_EQ(y, 21.0);
}

TEST(DataUtilTest, create2DArrayfromDatafield)
{
    Datafield out_data{
        {newEquiDivision("axis0", 2, 1.0, 2.0), newEquiDivision("axis1", 3, 3.0, 4.0)}};
    EXPECT_EQ(6u, out_data.size());

    EXPECT_EQ(2u, out_data.axis(0).size()); // no. of rows
    EXPECT_EQ(3u, out_data.axis(1).size()); // no. of cols

    std::vector<double> arr_in{1, 2, 3, 4, 5, 6};
    out_data.setVector(arr_in);

    EXPECT_EQ(arr_in[0], out_data[0]);
    EXPECT_EQ(arr_in[1], out_data[1]);
    EXPECT_EQ(arr_in[2], out_data[2]);
    EXPECT_EQ(arr_in[3], out_data[3]);
    EXPECT_EQ(arr_in[4], out_data[4]);
    EXPECT_EQ(arr_in[5], out_data[5]);

    auto arr_out = out_data.flatVector();
    EXPECT_EQ(arr_in, arr_out);

    auto array_2d = DataUtil::Data::create2DArrayfromDatafield(out_data);

    std::vector<std::vector<double>> array_expected_2d{{arr_in[0], arr_in[1], arr_in[2]},
                                                       {arr_in[3], arr_in[4], arr_in[5]}};
    EXPECT_EQ(array_expected_2d, array_2d);
}

TEST(DataUtilTest, vecvecToDatafield)
{
    std::vector<double> arr_in{1, 2, 3, 4, 5, 6};
    std::vector<std::vector<double>> array_2d{{arr_in[0], arr_in[1], arr_in[2]},
                                              {arr_in[3], arr_in[4], arr_in[5]}};
    auto data = DataUtil::Data::vecvecToDatafield(array_2d);
    EXPECT_EQ(arr_in, data->flatVector());
}
