#ifndef LEVMAR_FIT
#define LEVMAR_FIT

//#ifdef SWIGPYTHON
//class AdvLevmarFit;
//#else // SWIGPYTHON

#include <ctime>

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

#include "Header.hh"
#include "ThreadBase.hh"

#include "AdvMessage.hh"
#include "AdvVectorArrayConv.hh"
#include "AdvFuncBase.hh"
#include "AdvLevmarConsts.hh"
#include "AdvAdditionalData.hh"
#include "AdvLevmarControl.hh"
#include "AdvLevmarArgs.hh"
#include "AdvLevmarImmutables.hh"
#include "AdvConvergenceStat.hh"
#include "AdvFitCommand.hh"
#include "AdvLevmarFunc.hh"

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

    private:
        AdvLevmarImmutables *im;
        AdvConvergenceStat *stat;
        AdvFitCommand *command;

        /** informations for levmar */
        Double *param;
        /** covariance matrix */
        Double *covar;
        /** informations for levmar */
        Double *info;
        /** working area for levmar */
        Double *work;

        Double *linkids;

        Int4 remainder;
        Int4 iterationCounts;
        Int4 smallLoop;
        AdvLevmarConsts::LevmarStat terminationId;

    protected:
        void getCommand();
        void putConvergenceStat();

    public:
        AdvLevmarFit(AdvLevmarImmutables *im, AdvConvergenceStat *stat, AdvFitCommand *command);
        ~AdvLevmarFit();
    
        void Run();

    private:
        //static void *startRoutine(void *arg);

    private:
        void setLevmarImmutables(AdvLevmarImmutables *im);
        void setConvergenceStat(AdvConvergenceStat *stat);
        void setFitCommand(AdvFitCommand *command);
        /** allocate info for levmar */
        void allocInfo();
        /** allocate firring parameters */
        void allocFittingParam(AdvLevmarImmutables *im);
        /** allocate covariance matrix */
        void allocCovar(AdvLevmarImmutables *im);
        /** allocate working area */
        void allocWorkArea(AdvLevmarImmutables *im);

        void allocLinkIds(AdvLevmarImmutables *im);

        void initialise(AdvLevmarImmutables *im);

        Int4 doNoConstrain(const Bool useNumericalDiff, AdvLevmarArgs &args, const Int4 iterations);
        Int4 doBC(const Bool useNumericalDiff, AdvLevmarArgs &args, const Int4 iterations);
#ifdef HAVE_LAPACK
        Int4 doLEC(const Bool useNumericalDiff, AdvLevmarArgs &args, const Int4 iterations);
        Int4 doLIC(const Bool useNumericalDiff, AdvLevmarArgs &args, const Int4 iterations);
        Int4 doBLEC(const Bool useNumericalDiff, AdvLevmarArgs &args, const Int4 iterations);
        Int4 doBLIC(const Bool useNumericalDiff, AdvLevmarArgs &args, const Int4 iterations);
        Int4 doLEIC(const Bool useNumericalDiff, AdvLevmarArgs &args, const Int4 iterations);
        Int4 doBLEIC(const Bool useNumericalDiff, AdvLevmarArgs &args, const Int4 iterations);
#endif // HAVE_LAPACK

        Double getRFactor(AdvLevmarControl &control, AdvLevmarArgs &args, Double *param);

        void getNorms(
            void (*evalFunc)(Double *p, Double *hx, Int4 m, Int4 n, void *adata),
            void (*evalFuncJacobian)(Double *p, Double *j, Int4 m, Int4 n, void *adata),
            const AdvLevmarControl &control, const AdvLevmarArgs &args, Double *param,
            Double *residuErrNorm, Double *gradientNorm);

        //Double                   *vectorToArray(const std::vector<Double> *v);
        //std::vector<Double>           *arrayToVector(const Double *a, const Int4 n);
        //std::vector< std::vector<Double> > *arrayToMatrix(const Double *a, const Int4 n);
        std::vector<Double>           *covarToErrorVector(const Double *covar, const Int4 n);

        AdvParamSet *createStat( AdvLevmarControl &control,  AdvLevmarArgs &args, AdvLevmarConsts::LevmarStat,  Double *info,  Double *param,  Double *covar,Double *linkids, Int4 time);
        //void outputStat(AdvLevmarControl &control, AdvLevmarArgs &args, AdvLevmarConsts::LevmarStat stat, Double *info, Double *param);
        //void outputFittedParam(AdvLevmarControl &control, AdvLevmarArgs &args, Double *param, Double *covar);
        //void outputCovarianceMatrix(AdvLevmarArgs &args, Double *covar);
        //void output(AdvLevmarControl &control, AdvLevmarArgs &args, AdvLevmarConsts::LevmarStat stat, Double *info, Double *param, Double *covar);
};

//#endif // SWIGPYTHON

#endif // LEVMAR_FIT
