//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Base/Axis/FrameUtil.cpp
//! @brief     Implements namespace FrameUtil.
//!
//! @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/Axis/FrameUtil.h"
#include "Base/Axis/Frame.h"
#include "Base/Axis/Scale.h"

size_t FrameUtil::product_size(const std::vector<const Scale*>& axes)
{
    if (axes.empty())
        return 0;
    size_t result = 1;
    for (const Scale* ax : axes)
        result *= ax->size();
    return result;
}

// For axis EquiDivision("axis", 8, -5.0, 3.0) the coordinate x=-4.5 (center of bin #0) will
// be converted into 0.5 (which is a bin center expressed in bin fraction coordinates).
// The coordinate -5.0 (outside of axis definition) will be converted to -0.5
// (center of non-existing bin #-1).
// Used for Mask conversion.

double FrameUtil::coordinateToBinf(double coordinate, const Scale& axis)
{
    size_t index = axis.closestIndex(coordinate);
    Bin1D bin = axis.bin(index);
    double f = (coordinate - bin.lowerBound()) / bin.binSize();
    return static_cast<double>(index) + f;
}

double FrameUtil::coordinateFromBinf(double value, const Scale& axis)
{
    int index = static_cast<int>(value);

    double result(0);
    if (index < 0) {
        Bin1D bin = axis.bin(0);
        result = bin.lowerBound() + value * bin.binSize();
    } else if (index >= static_cast<int>(axis.size())) {
        Bin1D bin = axis.bin(axis.size() - 1);
        result = bin.upperBound() + (value - axis.size()) * bin.binSize();
    } else {
        Bin1D bin = axis.bin(static_cast<size_t>(index));
        result = bin.lowerBound() + (value - static_cast<double>(index)) * bin.binSize();
    }

    return result;
}

void FrameUtil::coordinatesToBinf(double& x, double& y, const Frame& frame)
{
    x = coordinateToBinf(x, frame.xAxis());
    y = coordinateToBinf(y, frame.yAxis());
}

void FrameUtil::coordinatesFromBinf(double& x, double& y, const Frame& frame)
{
    x = coordinateFromBinf(x, frame.xAxis());
    y = coordinateFromBinf(y, frame.yAxis());
}
