// SPDX-License-Identifier: LGPL-3.0-or-later
// Author: Kristian Lytje

#pragma once

#include <hist/intensity_calculator/CompositeDistanceHistogramFFExplicit.h>
#include <hist/intensity_calculator/crysol/FormFactorCrysol.h>

namespace ausaxs::hist {
    /**
     * @brief An alternative to CompositeDistanceHistogramFFExplicit that mimics the CRYSOL excluded volume fitting. 
     */
    class CompositeDistanceHistogramCrysol : public CompositeDistanceHistogramFFExplicitBase<form_factor::lookup::atomic::table_t, form_factor::lookup::cross::table_t, form_factor::lookup::exv::table_t>{
        public:
            CompositeDistanceHistogramCrysol();
            CompositeDistanceHistogramCrysol(const CompositeDistanceHistogramCrysol&);
            CompositeDistanceHistogramCrysol(CompositeDistanceHistogramCrysol&&) noexcept;
            CompositeDistanceHistogramCrysol& operator=(CompositeDistanceHistogramCrysol&&) noexcept;
            CompositeDistanceHistogramCrysol& operator=(const CompositeDistanceHistogramCrysol&);
            virtual ~CompositeDistanceHistogramCrysol() override;

            /**
             * @brief Create a new unweighted composite distance histogram with form factors.
             *        The same distance histogram is used for aa, ax, and xx interactions (with different form factor tables).
             *        Similarly, the same histogram is used for aw and wx interactions.
             * 
             * @param p_aa The partial distance histogram for atom-atom interactions (also used for ax and xx).
             * @param p_aw The partial distance histogram for atom-water interactions (also used for wx).
             * @param p_ww The partial distance histogram for water-water interactions.
             * @param p_tot The total distance histogram. This is only used for determining the maximum distance.
             * @param avg_displaced_V The average displaced volume per atom.
             */
            CompositeDistanceHistogramCrysol(
                hist::Distribution3D&& p_aa, 
                hist::Distribution2D&& p_aw, 
                hist::Distribution1D&& p_ww,
                hist::Distribution1D&& p_tot,
                double avg_displaced_V
            );

            /**
             * @brief Create a new weighted composite distance histogram with form factors.
             *        The same distance histogram is used for aa, ax, and xx interactions (with different form factor tables).
             *        Similarly, the same histogram is used for aw and wx interactions.
             * 
             * @param p_aa The partial distance histogram for atom-atom interactions (also used for ax and xx).
             * @param p_aw The partial distance histogram for atom-water interactions (also used for wx).
             * @param p_ww The partial distance histogram for water-water interactions.
             * @param p_tot The total distance histogram. This is only used to extract the bin centers.
             * @param avg_displaced_V The average displaced volume per atom.
             */
            CompositeDistanceHistogramCrysol(
                hist::Distribution3D&& p_aa, 
                hist::Distribution2D&& p_aw, 
                hist::Distribution1D&& p_ww, 
                hist::WeightedDistribution1D&& p_tot,
                double avg_displaced_V
            );

            const form_factor::lookup::atomic::table_t& get_ff_table() const override;
            const form_factor::lookup::cross::table_t& get_ffax_table() const override;
            const form_factor::lookup::exv::table_t& get_ffxx_table() const override;

            Limit get_excluded_volume_scaling_factor_limits() const override;

            /**
             * @brief Get the excluded volume scaling factor.
             *
             * @param cx The scaling factor for the excluded volume.
             * @param q The scattering vector.
             * @param avg_displaced_V The average displaced volume per atom.
             */
            static double exv_factor(double q, double cx, double avg_displaced_V);

            double average_displaced_V = 0;

        protected:
            double exv_factor(double q) const override;
            void initialize();

            inline static form_factor::lookup::atomic::table_t ffaa_table;
            inline static form_factor::lookup::cross::table_t  ffax_table;
            inline static form_factor::lookup::exv::table_t    ffxx_table;
    };
}