#ifndef SHAMOTODYMPDF
#define SHAMOTODYMPDF

#include "StringTools.hh"
#include "MlfArraySlicer.hh"
#include "UtsusemiHeader.hh"
#include "MlfPhysicalConstants.hh"
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <cmath>
#include <iomanip>

//////////////////////
//  ShamotoDymPDF
/////////////////////

//!
/*!
 *  # DymPDF
 *  Magnetic Form factor
 *  s = Q/(4*PI)
 *  A1*exp(-a2*s*s) + B1*exp(-b2*s*s) + C1*exp(-c2*s*s) + D
 *  SetMagFormFact(A1, a2, B1, b2, C1, c2, D)
 *
 *  ## Sample script for DymPDF
 *  DPDF = mu.ShamotoDymPDF()
 *  DPDF.SetModeToDymPDF()
 *  DPDF.SetData(DAT)
 *  DPDF.SetSliceWidth(0.2, 1.0, 12.5)
 *  DPDF.SetCf(74.84)
 *  DPDF.SetMagFormFact(0.4220, 17.684, 0.5948, 6.005, 0.0043, -0.609, -0.0219)
 *  DPDF.SetRRange(0.1, 40.0)
 *  DPDF.Execute()
 *  ECA = DPDF.PutResult(True)
 *
 *  # DyPDF
 *  ## Sample script for DyPDF
 *  DPDF = mu.ShamotoDymPDF()
 *  DPDF.SetModeToDyPDF() # execute as DyPDF
 *  DPDF.SetData(DAT)
 *  DPDF.SetSliceWidth(0.2, 1.0, 12.5)
 *  DPDF.SetRRange(0.1, 40.0)
 *  DPDF.SetNav(5)
 *  DPDF.Execute()
 *  ECA = DPDF.PutResult(True)
 */

class ShamotoMagFormFact
{
public:
    ShamotoMagFormFact();
    ~ShamotoMagFormFact();
    Double A1, a2, B1, b2, C1, c2, D;
    Double Calc(Double Q);
};

class ShamotoDymPDF
{
private:
    ElementContainerMatrix* _ecm;   /* Given Powder Data */
    ElementContainerArray* _sArray; /* Sliced Data Array */
    bool _is_inner_ecm;
    StringTools* _st;
    Double _dHW;
    Double _minHW, _maxHW;
    Double _Cf;
    Double _dR, _maxR;
    ShamotoMagFormFact* _mfg;
    bool _useMF;
    UInt4 _nav;
    std::string _MessageTag;
    bool _DEBUG;

public:
    ShamotoDymPDF(bool useMF=true);
        //!< Constructor
        /*!<
         */
    ~ShamotoDymPDF();
        //!< Destructor
        /*!<
         */
    void SetModeToDymPDF(){SetMode(true); }
        //!< Set mode to DymPDF.
        /*!<
         *   @param None
         *   @retval None
         */
    void SetModeToDyPDF(){SetMode(false); }
        //!< Set mode to DyPDF.
        /*!<
         *   @param None
         *   @retval None
         */
    void SetMode(bool useMF);
        //!< Set mode for DymPDF / DyPDF
        /*!<
         *   @param useMF (bool) True : DymPDF mode (with magnetic form factor), False : DyPDF mode
         *   @retval None
         */
    void SetData( ElementContainerMatrix* ecm );
        //!< Set data to be calculated.
        /*!<
         *   @param ecm (ElementContainerMatrix)
         *   @retval None
         */
    void SetData( ElementContainerArray* eca );
        //!< Set data to be calculated.
        /*!<
         *   @param eca (ElementContainerArray)
         *   @retval None
         */
    void SetSlicedData( ElementContainerArray* eca );
        //!< Set the empty data container to be stored the sliced data by the Execute method
        /*!<
         *   @param eca (ElementContainerArray)
         *   @retval None
         */

    void SetSliceWidth( Double delta_hw, Double min_hw=0.0, Double max_hw=0.0 );
        //!< Set parameters used for slicing along to energy axis
        /*!< min_hw == max_hw means the use of all energy reange in data.
         *
         *   @param delta_hw (Double) Energy bin width
         *   @param min_hw (Double) Minimun value of enargy range
         *   @param max_hw (Double) Maximun value of enargy range
         *   @retval None
         */
    void SetCf( Double cf ){ _Cf = cf; }
        //!< Set correction factor parameters used for the magnetic form factor calculation
        /*!<
         *   @param cf (Double)
         *   @retval None
         */
    void SetNav(UInt4 nav){ _nav = nav; }
        //!< Set the number of bins used for averaging intensity of S(Q,hw)
        /*!<
         *   @param nav (UInt4) the number of bins
         *   @retval None
         */
    void SetMagFormFact(Double _A1, Double _a2, Double _B1, Double _b2, Double _C1, Double _c2, Double _D);
        //!< Set parameters used for the magnetic form factor calculation
        /*!<  A1*exp(-a2*s*s) + B1*exp(-b2*s*s) + C1*exp(-c2*s*s) + D
         *    where s = Q/(4*PI)
         *
         *   @param _A1 (Double)
         *   @param _a2 (Double)
         *   @param _B1 (Double)
         *   @param _b2 (Double)
         *   @param _C1 (Double)
         *   @param _c2 (Double)
         *   @param _D (Double)
         *   @retval None
         */
    bool Slice( ElementContainerMatrix* ecm, ElementContainerArray* eca, Double delta_hw, Double min_hw=0.0, Double max_hw=0.0);
        //!< Do slice of the original data using the energy range and width given by SetSliceWidth.
        /*!< min_hw == max_hw means the use of all energy reange in data.
         *   G(r, E) is calcualted from this sliced data
         *
         *   @param ecm (ElementContainerMatrix) original data (S(Q,E))
         *   @param eca (ElementContainerArray) sliced data
         *   @param delta_hw (Double) Energy bin width
         *   @param min_hw (Double) Minimum value of enargy range
         *   @param max_hw (Double) Maximum value of enargy range
         *   @retval true succeeded.
         *   @retval false failed.
         */
    bool SetRRange( Double dR, Double maxR );
        //!< Set r range of calculated data G(r, E)
        /*!<
         *   @param dR (Double) bin width of r
         *   @param maxR (Double) Maximum value of r range
         *   @retval true succeeded.
         *   @retval false failed.
         */
    bool _ExecuteDyPDFBase(std::vector<Double>* r_RHO, std::vector<Double>* g_RHO, std::vector<Double>* e_RHO, std::vector<Double>* q_FQ, std::vector<Double>* s_FQ, std::vector<Double>* e_FQ, UInt4 nav);
        //!< Execute the DyPDF conversion calculation of G(r, E) from data S(Q,hw), hw is const.
        /*!<
         *   @param r_RHO (std::vector<Double>) r std::vector of G(r,hw) converted
         *   @param g_RHO (std::vector<Double>) Intensity std::vector of G(r,hw) converted
         *   @param e_RHO (std::vector<Double>) Error std::vector of G(r,hw) converted
         *   @param q_FQ (std::vector<Double>) Q std::vector of S(Q,hw)
         *   @param s_FQ (std::vector<Double>) Intensity std::vector of S(Q,hw)
         *   @param e_FQ (std::vector<Double>) Error std::vector of S(Q,hw)
         *   @param nav (UInt4) the number of bins for averaging
         *   @retval true succeeded.
         *   @retval false failed.
         */
    bool _ExecuteDyPDF( bool doSlice );
    bool _ExecuteDymPDF( bool doSlice );
    bool Execute( bool doSlice=true);
        //!< Execute the calculation of G(r, E) from the sliced data.
        /*!<
         *   @param doSlice (bool) plot S(Q, E) or not befor G(r, E) calculation.
         *   @retval true succeeded.
         *   @retval false failed.
         */

    bool _ExecuteWideDymPDFBase(std::vector<Double>& r_RHO, std::vector<Double>& g_RHO, std::vector<Double>& e_RHO, std::vector<Double>& q_FQ, std::vector<Double>& s_FQ, std::vector<Double>& e_FQ, const std::vector<Double>& params);
        //!< Execute the Wide-Q magnetic pair density funcation analysis (wideDymPDF) @ hw const
        /*!<
         *   @param r_RHO (std::vector<Double>) r vector of G(r,hw) converted
         *   @param g_RHO (std::vector<Double>) Intensity vector of G(r,hw) converted
         *   @param e_RHO (std::vector<Double>) Error vector of G(r,hw) converted
         *   @param q_FQ (std::vector<Double>) Q vector of S(Q,hw)
         *   @param s_FQ (std::vector<Double>) Intensity vector of S(Q,hw)
         *   @param e_FQ (std::vector<Double>) Error vector of S(Q,hw)
         *   @param params (std::vector<Double>) params[0]:Qmin for phonon background fitting, params[1]:Qmax for dynamic magnetic PDF analysis
         *   @retval true succeeded.
         *   @retval false failed.
         */
    bool ExecuteWideDymPDF(Double Qmin_phononBG, Double Qmax_DymPDF,  bool doSlice=true);
        //!< The Wide-Q dynamic magnetic pair density funcation analysis (wideDymPDF)
        /*!<
         *   @param Qmin_phononBG (Double) Qmin for phonon background fitting
         *   @param Qmax_DymPDF (Double) Qmax for dynamic magnetic PDF analysis
         *   @param doSlice (bool) plot S(Q, E) or not befor G(r, E) calculation
         *   @retval true succeeded
         *   @retval false failed
        */

    void TurnOffMF(bool off=true){ _useMF= !(off); }
        //!< Change the flag to use the magnetic form factor when G(r, E) calculation.
        /*!<
         *   @param off (bool) true means "Not use the magnetic form factor"
         *   @retval None
         */
    ElementContainerArray PutResult( bool isXaxisR = true);
        //!< Put the result G(r, E) or S(Q, E)
        /*!<
         *   @param isXaxisR (bool) true means G(r, E) returns, false means S(Q, E)
         *   @retval ElementContainerArray
         */
    void PutResult( ElementContainerArray* ret, bool isXaxisR = true);
        //!< Put the result G(r, E) or S(Q, E) into ElementContainerArray
        /*!<
         *   @param eca (ElementContainerArray) contains the result
         *   @param isXaxisR (bool) true means G(r, E) returns, false means S(Q, E)
         *   @retval None
         */
    ElementContainerArray PutSlicedMap(){ std::cout<<"############## PUT sArray ################";return *_sArray; }
        //!< Put the sliced data S(Q, E)
        /*!<
         *   @param None
         *   @retval None
         */

    bool Test( std::string _inputfile, std::string _outputfile, ElementContainerArray* _eca);
        //!< Test code
        /*!<
         *   @param _inputfile (std::string) test file path of sample 1d (hw, Intensity, Error) data at const hw
         *   @param _outputfile (std::string) output file path which is calcutated as G(r, hw)
         *   @retval true succeeded.
         *   @retval false failed.
         */
    bool TestDyPDF( std::string _inputfile, UInt4 nav );
        //!< Test code for DyPDF only
        /*!<
         *   @param _inputfile (std::string) test file path of sample 1d (hw, Intensity, Error) data at const hw
         *   @param nav
         *   @retval true succeeded.
         *   @retval false failed.
         */
    bool TestWideDymPDF( std::string _inputfile, Double Qmin, Double Qmax);
        //!< Test code for wideDymPDF only
        /*!<
         *   @param _inputfile (std::string) test file path of sample 1d (hw, Intensity, Error) data at const hw
         *   @param Qmin
         *   @param Qmax
         *   @retval true succeeded.
         *   @retval false failed.
         */
};

#endif
