/*
 * =====================================================================================
 *
 *       Filename:  ApproximateCoefficientFunction.h
 *
 *         Author:  Daniele Adani
 *
 *    Description:  L'artiglio che graffia
 *
 *  In this file there is the approximation for the unknown
 *  O(as^3) DIS massive coefficient functions.
 *
 * =====================================================================================
 */

#ifndef Approximate_h
#define Approximate_h

#include "adani/AsymptoticCoefficientFunction.h"
#include "adani/CoefficientFunction.h"
#include "adani/ExactCoefficientFunction.h"
#include "adani/ThresholdCoefficientFunction.h"

#include <memory>

//==========================================================================================//
//  class AbstractApproximate
//------------------------------------------------------------------------------------------//

class AbstractApproximate : public CoefficientFunction {
    public:
        AbstractApproximate(
            const int &order, const char &kind, const char &channel,
            const double &abserr = 1e-3, const double &relerr = 1e-3,
            const int &dim = 1000
        );
        ~AbstractApproximate() override = default;

        double GetAbsErr() const;
        double GetRelErr() const;
        int GetDim() const;

        void SetDoubleIntegralMethod(
            const DoubleIntegralMethod &double_int_method,
            const double &abserr = 1e-3, const double &relerr = 1e-3,
            const int &dim = 1000, const int &MCcalls = 25000
        );

        double MuIndependentTerms(double x, double m2Q2, int nf) const override;

        Value
            fxBand(double x, double m2Q2, double m2mu2, int nf) const override;
        double MuDependentTerms(
            double x, double m2Q2, double m2mu2, int nf
        ) const override;

    private:
        std::unique_ptr<ExactCoefficientFunction> muterms_;
};

//==========================================================================================//
//  class ApproximateCoefficientFunction
//------------------------------------------------------------------------------------------//

class ApproximateCoefficientFunction : public AbstractApproximate {
    public:
        ApproximateCoefficientFunction(
            const int &order, const char &kind, const char &channel,
            const bool &NLL = true,
            const HighScaleVersion &highscale_version = HighScaleVersion::Exact,
            const double &abserr = 1e-3, const double &relerr = 1e-3,
            const int &dim = 1000
        );
        ApproximateCoefficientFunction(const ApproximateCoefficientFunction &obj
        );
        ~ApproximateCoefficientFunction() override = default;

        bool GetNLL() const { return asymptotic_->GetNLL(); };
        HighScaleVersion GetHighScaleVersion() const {
            return asymptotic_->GetHighScaleVersion();
        };

        bool IsLegacyThreshold() const {
            return threshold_->IsLegacyThreshold();
        };
        bool IsLegacyPowerTerms() const {
            return asymptotic_->IsLegacyPowerTerms();
        };
        bool IsLegacyApproximation() const { return legacy_appr_; };

        void SetLegacyThreshold(const bool &legacy_threshold);
        void SetLegacyPowerTerms(const bool &legacy_pt);
        void SetLegacyApproximation(const bool &legacy_appr);

        Value MuIndependentTermsBand(
            double x, double m2Q2, int nf
        ) const override;

    private:
        std::unique_ptr<ThresholdCoefficientFunction> threshold_;
        std::unique_ptr<AsymptoticCoefficientFunction> asymptotic_;

        Value (ApproximateCoefficientFunction::*fx_)(double, double, int) const;

        bool legacy_appr_;

        std::unique_ptr<approximation_parameters> approximation_;
        std::unique_ptr<variation_parameters> variation_;

        double ApproximationLegacyForm(
            double x, double m2Q2, double asy, double thresh, double A,
            double B, double C, double D
        ) const;

        Value Approximation(double x, double m2Q2, int nf) const;
        Value ApproximationLegacy(double x, double m2Q2, int nf) const;

        void SetLegacyParameters();
};

//==========================================================================================//
//  class ApproximateCoefficientFunctionKLMV: Approximate coefficient functions
//  from [arXiv:1205.5727] klmv = Kawamura, Lo Presti, Moch, Vogt
//------------------------------------------------------------------------------------------//

class ApproximateCoefficientFunctionKLMV : public AbstractApproximate {
    public:
        ApproximateCoefficientFunctionKLMV(
            const int &order, const char &kind, const char &channel,
            const HighScaleVersion &highscale_version = HighScaleVersion::Exact,
            const bool &lowxi = false, const double &abserr = 1e-3,
            const double &relerr = 1e-3, const int &dim = 1000
        );
        ApproximateCoefficientFunctionKLMV(
            const ApproximateCoefficientFunctionKLMV &obj
        );
        ~ApproximateCoefficientFunctionKLMV() override = default;

        HighScaleVersion GetHighScaleVersion() const {
            return highscale_->GetHighScaleVersion();
        };
        bool GetLowXi() const { return lowxi_; }
        void SetLowXi(const bool &lowxi);

        Value MuIndependentTermsBand(
            double x, double m2Q2, int nf
        ) const override;

    private:
        std::unique_ptr<ThresholdCoefficientFunction> threshold_;
        std::unique_ptr<HighScaleCoefficientFunction> highscale_;
        std::unique_ptr<HighEnergyCoefficientFunction> highenergy_;

        Value (ApproximateCoefficientFunctionKLMV::*fx_)(
            double, double, int
        ) const;

        std::unique_ptr<klmv_params> params_A_;
        std::unique_ptr<klmv_params> params_B_;

        bool lowxi_;

        Value Order2(double x, double m2Q2, int nf) const;
        Value Order3(double x, double m2Q2, int nf) const;

        Value ApproximateNLL(double x, double m2Q2) const;

        void SetFunctions();
};

#endif
