//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Tests/Unit/PyBinding/EmbeddedTest.cpp
//!
//! @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 "Base/Const/Units.h"
#include "PyCore/Embed/PyInterpreter.h"
#include "PyCore/Embed/PyObjectPtr.h"
#include "Tests/GTestWrapper/google_test.h"
#include <string>
#include <vector>

//! Importing numpy and accessing its version string.

TEST(Embedded, PyInterpreterTest)
{
    // initialize Python interpreter
    PyInterpreter::initialize();
    EXPECT_TRUE(PyInterpreter::isInitialized());

    // add Python path
    PyInterpreter::addPythonPath("/Some/Extra/Python/Path/");
    EXPECT_FALSE(PyInterpreter::checkError());

    // get runtime info
    std::string runtime_info = PyInterpreter::runtimeInfo();
    std::cout << "Python runtime info:\n" << runtime_info << std::endl;

    // set Python path
    PyInterpreter::setPythonPath("/Some/Extra/Python/Path/");
}


TEST(Embedded, PyInterpreterNumpyTest)
{
    // TODO: re-implement the Numpy Test

    // initialize Python interpreter
    PyInterpreter::initialize();
    EXPECT_TRUE(PyInterpreter::isInitialized());

    // import Numpy
    PyObjectPtr numpy_module = PyInterpreter::import("numpy");
    EXPECT_TRUE(numpy_module.valid());
}


TEST(Embedded, BornAgainPyFunctionTest)
{
    // Test importing a Python script which uses BornAgain Python package

    // initialize Python interpreter
    PyInterpreter::initialize();
    EXPECT_TRUE(PyInterpreter::isInitialized());

    const std::string script{"import bornagain as ba; from bornagain import deg;"
                             "d0 = ba.deg; d1 = deg; "
                             "get_sample = lambda: (d0 == d1 and 'BornAgain.deg = %.3f' % d1)"};
    const std::string functionName{"get_sample"};

    // locate the `get_sample` function (it is an attribute of the module)
    PyObjectPtr ret{PyInterpreter::BornAgain::callScriptFunction(functionName, script, "")};

    if (!ret.valid())
        throw std::runtime_error("Failed executing Python function '" + functionName + "'");

    const std::string return_str = PyInterpreter::pyStrtoString(ret.get());
    std::stringstream _stream;
    _stream << std::fixed << std::setprecision(3) << Units::deg;
    const std::string expected_str = "BornAgain.deg = " + _stream.str();

    // verify that the returned string starts with the expected string
    if (!(return_str == expected_str))
        throw std::runtime_error("Result '" + return_str + "' does not match the expected '"
                                 + expected_str + "'");
}
