//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Device/Data/Transformer.cpp
//! @brief     Implements class Transformer.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2023
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#include "Device/Data/Transformer.h"
#include "Base/Axis/Frame.h"
#include "Base/Axis/MakeScale.h"
#include "Base/Axis/Scale.h"
#include "Device/Data/DataUtil.h"
#include "Device/Data/Datafield.h"
#include <cmath>
#include <numbers>
#include <stdexcept>

using std::numbers::pi;

Datafield Transformer::transformedDatafield(const Datafield& df) const
{
    auto conv0 = [wvl = m_wavelength, phi_i = m_phi_i](double phi_f) -> double {
        return 2 * pi / wvl * (std::sin(phi_f) - std::sin(phi_i));
    };
    auto conv1 = [wvl = m_wavelength, alpha_i = m_alpha_i](double alpha_f) -> double {
        return 2 * pi / wvl * (std::sin(alpha_f) + std::sin(alpha_i));
    };

    const Scale& xin = df.xAxis();
    const Scale& yin = df.yAxis();

    if (xin.axisLabel() != "phi_f (rad)")
        throw std::runtime_error("Cannot convert from '" + xin.axisLabel()
                                 + "' where 'phi_f (rad)' was expected");
    if (yin.axisLabel() != "alpha_f (rad)")
        throw std::runtime_error("Cannot convert from '" + yin.axisLabel()
                                 + "' where 'alpha_f (rad)' was expected");
    Scale* xout = EquiDivision("q_y (1/nm)", xin.size(), conv0(xin.min()), conv0(xin.max()));
    Scale* yout = EquiDivision("q_z (1/nm)", yin.size(), conv1(yin.min()), conv1(yin.max()));

    return {Frame(xout, yout), df.flatVector(), df.errorSigmas()};
}


Transformer FrameTrafo::ScatteringToQ(double wavelength, double alpha_i, double phi_i)
{
    return Transformer(wavelength, alpha_i, phi_i);
}

Datafield FrameTrafo::Transpose(const Datafield& df)
{
    double2d_t transposed_2d = DataUtil::transpose(df.values2D());
    return {Frame(df.yAxis().clone(), df.xAxis().clone()), FieldUtil::flatten(transposed_2d)};
}
