#ifndef MULTI_DATA_LEVMAR
#define MULTI_DATA_LEVMAR

#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 "ElementContainerMatrix.hh"

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

#include "AdvMultiDataMethod.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

/**
 *  fit one set of fitting parameters to muliple series of data
 *
 */
class AdvMultiDataLevmar : public AdvLevmarConsts, public AdvMultiDataMethod, public AdvMessage {

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

    protected:
        pthread_t ThreadDescriptor;

        vector< vector<Double> > srcBin;
        vector< vector<Double> > srcX;
        vector< vector<Double> > resultBin;
        vector< vector<Double> > resultX;
        vector< vector<Double> > resultY;
        vector< vector<Double> > resultE;
        //vector< vector<Double> > dataWeight;

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

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

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

        /** get the number of convergence states in the history buffer */
        UInt4 getHistorySize() const ;

        ////////////////    control methods    ////////////////

        /** set default values base on source data */
        AdvParamSet setDefaultParam(ElementContainerArray &src);

        /** check parameter's consistency  */
        Bool checkParam( ElementContainerArray &src, vector<AdvDomain> &domainArray, AdvParamSet &param) ;

        /** translate  parameters to the innder form  */
        void toInnerForm(ElementContainerArray &src, vector<AdvDomain> &domainArray, 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 results and data in progress    ////////////////

        /** put the results to a ElementContainer */
        void toElementContainer(ElementContainerArray &src, UInt4 id, ElementContainer &dest) const ;
        /** put the results to a ElementContainerArray */
        void toElementContainerArray(ElementContainerArray &src, ElementContainerArray &dest) const ;

        /** get Fitted Parameters */
        AdvParamSet getFittedParam() const ;

        /** get the increase-decrease table of the all fitting function */
        vector< vector<Double> > getTrend(UInt4 id);
        /** get the increase-decrease table of the all fitting function */
        vector< vector<Double> > getTrend();

        /** get the latest convergence state */
        AdvParamSet *getLatestConvergenceStat() const;

        /** get the convergence history */
        deque<AdvParamSet*> getConvergenceHistory() const ;

        /** get a list of the values for the specified key */
        vector<Int4>   getConvergenceHistoryForInt4(  const string &key) const ;
        /** get a list of the values for the specified key */
        vector<Double> getConvergenceHistoryForDouble(const string &key) const ;

        ////////////////    get results and data in progress    ////////////////
        void getParamDescription() const {};

    protected:
        Double likehood();

    private:

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

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

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

#endif // METHODTEST
