#ifndef LEVMAR_ARGS
#define LEVMAR_ARGS

//#ifdef SWIGPYTHON
//class AdvLevmarArgs;
//#else // SWIGPYTHON

/* headrs for levmar */
#include "compiler.h"
#include "levmar.h"
#include "lm.h"
#include "misc.h"

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

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

#include "AdvLevmarConsts.hh"
#include "AdvLevmarControl.hh"
#include "AdvAdditionalData.hh"

class AdvLevmarArgs : public AdvMessage, public AdvVectorArrayConv {
    private:
        static const std::string className; // =std::string("AdvLevmarArgs");

    public:
        /** the number of data series */
        UInt4 nSeries;

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

        /** the number of fitting parameters */
        Int4    nParam;
        /** the number of reference data */
        Int4    nRef;
        /** the initial values of the fitting parameters */
        Double *param;
        /** the initial values of the fitting parameters */
        Double *linkids;
        /** the values of the reference data */
        Double *ref;
        /** the lower bounes of the fitting parameters */
        Double *lb;
        /** the upper bounes of the fitting parameters */
        Double *ub;
#ifdef HAVE_LAPACK
        /** the weight of each box */
        Double *wghts;
        /** the number of the linear equations */
        UInt4   nEq;
        /** the matris constructed with the coefficients of the linear equations */
        Double *A;
        /** the vector of the constant term of the linear equations */
        Double *b;
        /** the number of the linear inequalities */
        UInt4   nIneq;
        /** the matris constructed with the coefficients of the linear inequalities */
        Double *C;
        /** the vector of the constant term of the linear inequalities */
        Double *d;
#endif // HAVE_LAPACK
        /** options for Levmar,
         *  opts[0]          initial value of scaling factor,
         *  opts[1]-opts[3]  stopping thresholds,
         *  opts[4]          delta for numerical differentiation
         *
         *  \see levmar sources: lm_core.c, lmbc_core.c, lmblec_core.c, lmbleic_core.c
         */
        Double *opts;
        /** data to evaluate the values of the fitting function */
        AdvAdditionalData *additionalData;

    public:
        /** constructor */
        AdvLevmarArgs();
        /** destructor */
        ~AdvLevmarArgs();

        /** check parameters for single data series */
        Bool checkParam(ElementContainer &src,      AdvDomain &AdvDomain,              AdvParamSet &param, AdvLevmarControl &control);
        /** check parameters for multiple data series */
        Bool checkParam(ElementContainerArray &src, std::vector<AdvDomain> &AdvDomainArray, AdvParamSet &param, AdvLevmarControl &control);

        /** translate single data series to inner form */
        void toInnerForm(ElementContainer      &src, AdvDomain         &AdvDomain,      AdvParamSet &param, AdvLevmarControl &control);
        /** translate multiple data series to inner form */
        void toInnerForm(ElementContainerArray &src, std::vector<AdvDomain> &AdvDomainArray, AdvParamSet &param, AdvLevmarControl &control);

        /** output */
        //void output();

    private:
        ////////         checkers        ////////
        Bool checkFunc(const AdvParamSet &param);    // only for single sata series
        Bool checkFuncSet(const AdvParamSet &param); // only for multi data series

        Bool checkNumberOfFittingParam(const AdvParamSet &pram);               // only for single data series
        Bool checkNumberOfFittingParamForMultiFunc(const AdvParamSet &pram);   // only for multi data series

        // common functions for single data series and multi data series
        Bool checkConstrainParam(const AdvParamSet &param, AdvLevmarControl &constrol);
        Bool checkBoxies(const AdvParamSet &param);
#ifdef HAVE_LAPACK
        Bool checkBoxWeights(const AdvParamSet &param);
        Bool checkEquations(const AdvParamSet &param);
        Bool checkInequalities(const AdvParamSet &param);
#endif // HAVE_LAPACK
        Bool checkScalingFactor(const AdvParamSet &param);
        Bool checkSingleThreshold(const AdvParamSet &param, const std::string &key, const std::string &deprecatedKey);
        Bool checkThresholds(const AdvParamSet &param);
        Bool checkDiffDelta(const AdvParamSet &param);

        ////////        setters        ////////
        std::vector<AdvFuncBase*>           setFuncList(const AdvParamSet &param);    // for single data series only
        std::vector< std::vector<AdvFuncBase*> > setFuncMatrix(const AdvParamSet &param);  // for multi  data series only

        Int4               setNumberOfRequiredParam(const AdvParamSet &param);                   // for single data seires only
        std::vector<Int4>       setListOfNumberOfRequiredParamForMultiFunc(const AdvParamSet &param); // for multi  data seties only
        Int4               setNumberOfRequiredParamForMultiFunc(const AdvParamSet &param);       // for multi  data seties only

        Int4               setNumberOfReferenceData(ElementContainer      &src, const AdvDomain         &AdvDomain);       // for single data series only
        Int4               setNumberOfReferenceData(ElementContainerArray &src, const std::vector<AdvDomain> &AdvDomainArray);  // for muplt data series  only

        void               setConstrainParams(const AdvParamSet &param, const AdvLevmarControl &constrol);
        Int4               setNumberOfFittingParam(const AdvParamSet &param);
        Double            *setFittingParamArray(const AdvParamSet &param);
        Double            *setLinkIdsArray(const AdvParamSet &param);

        Double            *setReferenceDataArray(ElementContainer      &src, const AdvDomain         &AdvDomain,      const AdvParamSet &param); // for single data series
        Double            *setReferenceDataArray(ElementContainerArray &src, const std::vector<AdvDomain> &AdvDomainArray, const AdvParamSet &param); // for multi data series

        std::vector<Double>    *setDataWeightArray(ElementContainer      &src, const AdvDomain         &AdvDomainr,     const AdvParamSet &param, const AdvLevmarControl &control); // for single data series
        std::vector<Double>    *setDataWeightArray(ElementContainerArray &src, const std::vector<AdvDomain> &AdvDomainArray, const AdvParamSet &param, const AdvLevmarControl &control); // for multiple data series

        Double            *set1dArray(const AdvParamSet &param, const std::string &key);
        Double            *setLowerBounds(const AdvParamSet &param);
        Double            *setUpperBounds(const AdvParamSet &param);
#ifdef HAVE_LAPACK
        Double            *setBoxWeights(const AdvParamSet &param);
        Double            *set2dArray(const AdvParamSet &param, const std::string &key);
        Double            *setConstArray(const AdvParamSet &param, const std::string &key);
        Int4               setNumberOfEquations(const AdvParamSet &param);
        Double            *setEquationCoefficients(const AdvParamSet &param);
        Double            *setEquationConsts(const AdvParamSet &param);
        Int4               setNumberOfInequalities(const AdvParamSet &param);
        Double            *setInequalityCoefficients(const AdvParamSet &param);
        Double            *setInequalityConsts(const AdvParamSet &param);
#endif // HAVE_LAPACK
        Double            *setOptions(const AdvParamSet &param);
        //AdditionalData    *createAdditionalData(ElementContainer      &src, AdvDomain         &AdvDomain,      AdvParamSet &param);
        //AdditionalData    *createAdditionalData(ElementContainerArray &src, std::vector<AdvDomain> &AdvDomainArray, AdvParamSet &param, AdvLevmarControl &control);

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

    public:
        std::string         *getFunctionStr();
        std::string         *getFunctionStrForMultiData(UInt4 i);

        ////////         getter        ////////
        std::vector<Double> *getInitialParam();
        std::vector<Double> *getLowerBounds();
        std::vector<Double> *getUpperBounds();
#ifdef HAVE_LAPACK
        std::vector<Double> *getBoxWeights();
        std::vector< std::vector<Double> > *getEqMatrix();
        std::vector< std::vector<Double> > *getIneqMatrix();
#endif // HAVE_LAPACK

    public:
        ////////        output        ////////
        void outputConstrainBox(AdvLevmarConsts::Constrain constrain);
#ifdef HAVE_LAPACK
        void outputConstrainExpressions(const Int4 nExp, const Int4 nParam, const Double *A, const Double *b);
#endif // HAVE_LAPACK
        void outputInitialFittingParam();
        void outputOptions(const AdvLevmarControl &control);
        void outputAdditionalData();
        void outputReferenceData();
};

//#endif // SWIGPYTHON

#endif //  LEVMAR_ARGS
