//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Tests/Functional/CoreSpecial/FourierTransformationTest.cpp
//! @brief     Implements FourierTransformationTest class.
//!
//! @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 "BATesting.h"
#include "Base/Util/PathUtil.h"
#include "Device/Data/DataUtil.h"
#include "Device/Data/Datafield.h"
#include "Device/Histo/DiffUtil.h"
#include "Device/IO/IOFactory.h"
#include "Tests/GTestWrapper/google_test.h"
#include <iostream>
#include <memory>
#include <vector>

namespace {

const double threshold = 1e-10;

//! Returns name of fft image based on given image name.
std::string fftReferenceImage(const std::string& input_image)
{
    auto filename = Base::Path::filename(input_image);
    return Base::Path::jointPath(BATesting::ReferenceDir_CoreSpecial(),
                                 "FourierTransformation_" + filename);
}

//! Runs test over one image. Returns true upon success.
bool test_fft(const std::string& input_image_name, const std::string& reference_fft_name)
{
    std::cout << "Input image: " << input_image_name << std::endl;
    std::cout << "Reference fft: " << reference_fft_name << std::endl;

    // loading input image
    std::unique_ptr<Datafield> input_image;
    try {
        const auto filename =
            Base::Path::jointPath(BATesting::ReferenceDir_Suite(), input_image_name);
        input_image.reset(IO::readData2D(filename));
    } catch (const std::exception&) {
        std::cout << "Error: no input image.\n";
        return false;
    }

    std::cout << "transforming" << std::endl;
    std::unique_ptr<Datafield> fft = DataUtil::Data::createFFT(*input_image);

    std::cout << "loading reference" << std::endl;
    std::unique_ptr<Datafield> reference_fft;
    try {
        reference_fft.reset(IO::readData2D(reference_fft_name));
    } catch (const std::exception&) {
        std::cout << "Error: no reference fft image. Creating new one.\n";
    }

    std::cout << "comparing" << std::endl;
    bool success(false);
    if (reference_fft)
        success =
            DiffUtil::meanRelVecDiff(fft->flatVector(), reference_fft->flatVector()) <= threshold;

    if (!success) {
        Base::Path::createDirectory(BATesting::TestOutDir_Core());
        std::string out_fname = Base::Path::jointPath(BATesting::TestOutDir_Core(),
                                                      Base::Path::filename(reference_fft_name));
        IO::writeDatafield(*fft, out_fname);
        std::cout << "New fft image stored in " << out_fname << std::endl;
    }

    return success;
}

} // namespace

TEST(FourierTransformationTest, FourierTransformation)
{
    for (const char* inputImage : {"CylindersAndPrisms.int.gz", "RectDetectorGeneric.int.gz"})
        EXPECT_TRUE(test_fft(inputImage, fftReferenceImage(inputImage)));
}
