#ifndef NEWLEVMAR
#define NEWLEVMAR

#include <cmath>
#include <cstdlib>
#ifdef USE_PTHREAD
#include <pthread.h>
#endif // USE_PTHREAD

#include <exception>
#include <deque>

/* 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 "AdvFuncBase.hh"
#include "AdvFuncComb.hh"
#include "AdvDomain.hh"
#include "AdvParamSet.hh"

#include "AdvMethod.hh"
#include "AdvLevmarConsts.hh"
#include "AdvAdditionalData.hh"
#include "AdvLevmarControl.hh"
#include "AdvLevmarArgs.hh"
#include "AdvLevmarImmutables.hh"
#include "AdvConvergenceStat.hh"
#include "AdvFitCommand.hh"
#include "AdvLevmarFit.hh"
#include "AdvReportConvergenceProcess.hh"

#define USE_PTHREAD

class AdvNewLevmar : public AdvLevmarConsts, public AdvMethod, public AdvMessage {

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

    protected:
        pthread_t ThreadDescriptor;

        std::vector<Double> srcBin;
        std::vector<Double> srcX;
        std::vector<Double> resultBin;
        std::vector<Double> resultX;
        std::vector<Double> resultY;
        std::vector<Double> resultE;

        AdvLevmarImmutables *im;
        AdvConvergenceStat *stat;
        AdvConvergenceStat *history;
        AdvFitCommand *command;
#ifdef USE_PTHREAD
        pthread_t thread;
#endif // USE_PTHREAD

    public:
        AdvNewLevmar();
        ~AdvNewLevmar();

        /** set default values base on source data */
        AdvParamSet setDefaultParam(ElementContainer &src);
        /** check parameter's consistency  */
        Bool checkParam( ElementContainer &src, AdvDomain &AdvDomain, AdvParamSet &param) ;
        /** translate  parameters to the innder form  */
        void toInnerForm(ElementContainer &src, AdvDomain &AdvDomain, AdvParamSet &param) ;

        /** start fitting */
        void fit();
        /** true if fitting is running*/
        Bool isFitting();
        /** stop fitting */
        void stopFit();

        /** evaluate the values of the fitting function */
        void eval();
        /** get the increase-decrease table of the fitting function */
        std::vector< std::vector<Double> > getTrend();
        /** put the results to a ElementContainer */
        void toElementContainer(ElementContainer &src, ElementContainer &dest) const ;
        /** get Fitted Parameters */
        AdvParamSet getFittedParam() const ;

        /** differentiable */
        Bool differentiable() const { return false; } ;
        /** support muliti thread */
        Bool isMultiThreaded() const { return true; } ;

        /** get the latest convergence state */
        AdvParamSet *getLatestConvergenceStat() const;
        /** get the convergence history */
        std::deque<AdvParamSet*> getConvergenceHistory() const ;
        /** get the number of convergence states in the history buffer */
        UInt4 getHistorySize() const ;
        /** get a list ot the values for the specified key */
        std::vector<Int4>   getConvergenceHistoryForInt4(  const std::string &key) const ;
        /** get a list ot the values for the specified key */
        std::vector<Double> getConvergenceHistoryForDouble(const std::string &key) const ;

        void getParamDescription() const {};
        /*
        ElementContainer getResult();
        ElementContainerArray getResultComponent();
        */

    protected:
        Double likehood();

    private:

        /* subcontract for toInnerForm */
        std::vector<Double>     getFittingParam(const AdvParamSet &param);

        /* */
        void addExpressionsToHeader(ElementContainer &dest, const std::string &keyBase, std::vector< std::vector<Double> > *expressions) const ;

        /* subcontract for fit */
        //void fittingInitial(AdvLevmarImmutables *im);
        //void fittingResult(AdvLevmarImmutables *im, AdvConvergenceStat *stat);
};

#endif // METHODTEST
