//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sample/Aggregate/InterferenceFunction3DLattice.cpp
//! @brief     Implements class InterferenceFunction3DLattice.
//!
//! @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 "Sample/Aggregate/InterferenceFunction3DLattice.h"
#include "Base/Utils/Assert.h"
#include "Sample/Correlations/IPeakShape.h"
#include <algorithm>
#include <iostream>

InterferenceFunction3DLattice::InterferenceFunction3DLattice(const Lattice3D& lattice)
    : IInterferenceFunction(0), m_lattice(lattice), m_peak_shape(nullptr), m_rec_radius(0.0)
{
    setName("Interference3DLattice");
    initRecRadius();
}

InterferenceFunction3DLattice::~InterferenceFunction3DLattice() = default;

InterferenceFunction3DLattice* InterferenceFunction3DLattice::clone() const
{
    auto* ret = new InterferenceFunction3DLattice(m_lattice);
    ret->setPositionVariance(m_position_var);
    if (m_peak_shape)
        ret->setPeakShape(*m_peak_shape);
    return ret;
}

void InterferenceFunction3DLattice::setPeakShape(const IPeakShape& peak_shape)
{
    m_peak_shape.reset(peak_shape.clone());
}

const Lattice3D& InterferenceFunction3DLattice::lattice() const
{
    return m_lattice;
}

std::vector<const INode*> InterferenceFunction3DLattice::getChildren() const
{
    return {};
}

void InterferenceFunction3DLattice::onChange()
{
    initRecRadius();
}

double InterferenceFunction3DLattice::iff_without_dw(const kvector_t q) const
{
    ASSERT(m_peak_shape);
    kvector_t center = q;
    double radius = 2.1 * m_rec_radius;
    double inner_radius = 0.0;
    //    if (m_peak_shape->angularDisorder()) {
    //        center = kvector_t(0.0, 0.0, 0.0);
    //        inner_radius = std::max(0.0, q.mag() - radius);
    //        radius += q.mag();
    //    }
    const auto rec_vectors = m_lattice.reciprocalLatticeVectorsWithinRadius(center, radius);
    // std::cout << "IWD: q=" << q << ", r=" << radius << ", ir=" << inner_radius <<
    //    ", disordered=" << m_peak_shape->angularDisorder() << " => nVec=" << rec_vectors.size()
    //          << std::endl;

    double result = 0.0;
    for (const auto& q_rec : rec_vectors)
        //        if (!(q_rec.mag() < inner_radius)) {
        // result += m_peak_shape->evaluate(q, q_rec);
        result += exp(-(q - q_rec).mag2() / 2 / pow(.06, 2));
    // std::cout << "  INCR: qr=" << q_rec << " => " << result << std::endl;
    //        }
    return result;
}

void InterferenceFunction3DLattice::initRecRadius()
{
    kvector_t a1 = m_lattice.getBasisVectorA();
    kvector_t a2 = m_lattice.getBasisVectorB();
    kvector_t a3 = m_lattice.getBasisVectorC();

    m_rec_radius = std::max(M_PI / a1.mag(), M_PI / a2.mag());
    m_rec_radius = std::max(m_rec_radius, M_PI / a3.mag());
}
