//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Base/Pixel/RectangularPixel.cpp
//! @brief     Implements class RectangularPixel.
//!
//! @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/Pixel/RectangularPixel.h"
#include "Base/Axis/MakeScale.h"
#include <numbers>
using std::numbers::pi;

RectangularPixel::RectangularPixel(const R3& corner_pos, const R3& width, const R3& height)
    : m_corner_pos(corner_pos)
    , m_width(width)
    , m_height(height)
    , m_normal(width.cross(height))
{
    // TODO URGENT: why allow solid angle <=0 ??
    auto solid_angle_value = calculateSolidAngle();
    m_solid_angle = solid_angle_value <= 0.0 ? 1.0 : solid_angle_value;
}

RectangularPixel* RectangularPixel::clone() const
{
    return new RectangularPixel(m_corner_pos, m_width, m_height);
}

RectangularPixel* RectangularPixel::createZeroSizePixel(double x, double y) const
{
    return new RectangularPixel(getPosition(x, y), R3(), R3());
}

R3 RectangularPixel::getK(double x, double y, double wavelength) const
{
    R3 direction = getPosition(x, y);
    double length = (2 * pi) / wavelength;
    return normalizeLength(direction, length);
}

R3 RectangularPixel::getPosition(double x, double y) const
{
    return m_corner_pos + x * m_width + y * m_height;
}

double RectangularPixel::integrationFactor(double x, double y) const
{
    if (m_solid_angle == 0.0)
        return 1.0;
    R3 position = getPosition(x, y);
    double length = position.mag();
    return std::abs(position.dot(m_normal)) / std::pow(length, 3) / m_solid_angle;
}

double RectangularPixel::solidAngle() const
{
    return m_solid_angle;
}

R3 RectangularPixel::normalizeLength(const R3 direction, double length) const
{
    return direction.unit_or_throw() * length;
}

double RectangularPixel::calculateSolidAngle() const
{
    R3 position = getPosition(0.5, 0.5);
    double length = position.mag();
    return std::abs(position.dot(m_normal)) / std::pow(length, 3);
}

Scale* RectangularPixel::createAxis(size_t n) const
{
    const auto k00 = getPosition(0.0, 0.0);
    const auto k01 = getPosition(0.0, 1.0);
    const double alpha_f_min = (pi / 2) - R3Util::theta(k00);
    const double alpha_f_max = (pi / 2) - R3Util::theta(k01);

    return newEquiDivision("alpha_f", alpha_f_min, alpha_f_max, n);
}
