#ifndef SIMULATEDANNEALING_ARGS
#define SIMULATEDANNEALING_ARGS

#include "Header.hh"
#include "ElementContainer.hh"
#include "ElementContainerArray.hh"

#include "AdvMessage.hh"
#include "AdvDomain.hh"
#include "AdvParamSet.hh"

#include <gsl/gsl_rng.h>
#include <gsl/gsl_siman.h>

#include "AdvSimulatedAnnealingConsts.hh"

class AdvFuncBase;
class AdvSimulatedAnnealing;

class AdvSimulatedAnnealingArgs : public AdvMessage {

    friend class AdvSimulatedAnnealing;

    public:
        /** constructor */
        AdvSimulatedAnnealingArgs();

        /** destructor */
        ~AdvSimulatedAnnealingArgs();

        void SetCurParam(Double* v, Int4 n) { cur_param.resize(n); for (Int4 i=0;i<n;++i) cur_param[i]=v[i]; }
        void SetCurParamBest() { best_param = cur_param; }

    private:
        static const string className; // =string("AdvSimulatedAnnealingArgs");

        /** a list of fitting functions */
        vector<AdvFuncBase*> funcList;

        /** a matrix of fitting functions (for multi-fit) */
        vector< vector<AdvFuncBase*> > funcMatrix;

        /* the number of fitting parameters */
        Int4    nParam;

        /* the number of reference data */
        vector<Int4>  nRef;

        /* the initial values of the fitting parameters */
        vector<Double> param;
        vector<Double> cur_param;

        /* the values of the reference data */
        Int4    n_data;
        vector< vector<Double> >  ref;
        vector< vector<Double> >  err;

        vector< vector<Double> >  srcBin;
        vector< vector<Double> >  srcX;

        vector<Double> best_param;

        vector< vector<Double> >  bestY;

        Int4 ncall;
        Double min_chi2;

        /* the lower bounes of the fitting parameters */
        vector<Double> lb;

        /* the upper bounes of the fitting parameters */
        vector<Double> ub;

        /** constrain type */
        AdvSimulatedAnnealingConsts::Constrain  constrain;

        /** flag for Least Square method with data weight */
        Bool useDataWeights;

        /* output interval */
        Int4 outputInterval;

        /** GSL: random number generator */
        gsl_rng *rand;

        /** GSL: parameter for gsl_siman_solve function */
        gsl_siman_params_t  siman_params;

        AdvParamSet setDefaultParam();
        AdvParamSet setDefaultParam(ElementContainer &src);
        AdvParamSet setDefaultParam(ElementContainerArray &src);

        /** check parameters */
        Bool checkParam(ElementContainer &src, AdvDomain &domain, AdvParamSet &param);
        Bool checkParam(ElementContainerArray &src, vector<AdvDomain> &domainArray, AdvParamSet &param);

        /* check parameter */
        Bool checkConstrainType(const AdvParamSet &param);
        Bool checkUseDataWeights(const AdvParamSet &param);
        Bool checkNumberOfFittingParam(const AdvParamSet &pram);
        Bool checkBoxies(const AdvParamSet &param);

        /** translate to inner form */
        void toInnerForm(ElementContainer &src, AdvDomain &domain, AdvParamSet &param);
        void toInnerForm(ElementContainerArray &src, vector<AdvDomain> &domain, AdvParamSet &param);
        /** translate into the inner form */
        void toInnerForm(const AdvParamSet &param);

        AdvParamSet getBestParam() const;

        vector<Double> getInitialParam()  { return param; }
        vector<Double> getLowerBounds()   { return lb; }
        vector<Double> getUpperBounds()   { return ub; }

        /* translate to inner form */
        AdvSimulatedAnnealingConsts::Constrain  getConstrain(const AdvParamSet &param);

        enum CHI2MODE {
            USE_GIVEN_ERRORS,
            USE_Y_INVERSE,
            USE_LOG,
            NO_WEIGHT
        };

        enum CHI2MODE  chi2mode;


        vector<AdvFuncBase*>           setFuncList(const AdvParamSet &param);
        vector< vector<AdvFuncBase*> > setFuncMatrix(const AdvParamSet &param);
        Int4               setNumberOfRequiredParam(const AdvParamSet &param);
        Int4               setNumberOfFittingParam(const AdvParamSet &param);
        // Int4               setNumberOfReferenceData(const ElementContainer &src, const AdvDomain &domain);
        // Double            *setFittingParamArray(const AdvParamSet &param);
        // Double            *setReferenceDataArray(ElementContainer &src, const AdvDomain &domain, const AdvParamSet &param);
        // Double            *setDataWeightArray(ElementContainer &src, const AdvDomain &domain);

        /**  */
        // vector< vector<Double> > getExtCoeffMatrix(const Double *A, const Double *b, const Int4 nEq, const Int4 nParam);
};

#endif //  SIMULATEDANNEALING_ARGS
