#ifndef SIMULATEDANNEALING
#define SIMULATEDANNEALING

#include <cmath>
#include <cstdlib>
#include <pthread.h>

#include <exception>

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

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

#include "AdvSimulatedAnnealingConsts.hh"
#include "AdvSimulatedAnnealingArgs.hh"
#include "ThreadBase.hh"

class AdvSimulatedAnnealing;

class simanXP {
public:
    simanXP() : siman(NULL),p(NULL),np(0) {}
    ~simanXP() { if (p) delete [] p; }

    AdvSimulatedAnnealing *siman;

    Double *p;   //  parameters (double)
    Int4    np;  //  number of parameters
};

class AdvSimulatedAnnealing : public ThreadBase, public AdvSimulatedAnnealingConsts, public AdvMessage, public AdvMethod  {

    public:
        AdvSimulatedAnnealing();
        ~AdvSimulatedAnnealing();

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

    protected:

        AdvSimulatedAnnealingArgs *args;

        enum SimAnStatus {
            CLEAR,
            READY,
            FITTING,
            FIT_SUSPENDED,
            FIT_DONE,
        };

        enum SimAnStatus  status;

        enum SimAnCommand {
            CONTINUE,
            SUSPEND,
            STOP,
        };

        enum SimAnCommand  command;

        enum FitMode {
            SingleFit,
            MultiFit,
        };

        enum FitMode  fmode;

    public:

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

        /** check parameter's consistency  */
        Bool checkParam( ElementContainer &src, AdvDomain &domain, AdvParamSet &param) ;
        Bool checkParam( ElementContainerArray &src, std::vector<AdvDomain> &domain, AdvParamSet &param) ;
        /** translate  parameters to the innder form  */
        void toInnerForm(ElementContainer &src, AdvDomain &domain, AdvParamSet &param) ;
        void toInnerForm(ElementContainerArray &src, std::vector<AdvDomain> &domain, AdvParamSet &param) ;
        /** start fitting */
        void fit();
        /** true if fitting is running*/
        Bool isFitting();
        /** stop fitting */
        void stopFit();
        void abortFit();
        void suspendFit();
        void eval();

        void toElementContainer(ElementContainer &src, ElementContainer &dest) const ;
        void toElementContainer(ElementContainerArray &src, UInt4 id, ElementContainer &dest) const ;
        void toElementContainerArray(ElementContainerArray &src, ElementContainerArray &dest) const ;

        AdvParamSet getFittedParam() const ;
        Bool differentiable() const { return false; } ;
        Bool isMultiThreaded() const { return true; } ;
        void getParamDescription() const { };
        AdvParamSet *getLatestConvergenceStat() const { return NULL; }

        void Run();
        std::vector< std::vector<Double> > getTrend() { return std::vector< std::vector<Double> >(); };

        void PrintStatus();
        int GetStatus();

    protected:
        Double likehood();

    private:
        static void   XPCOPY(void *source, void *dest) ;
        static void*  XPNEW(void *xp) ;
        static void   XPDELATE(void *xp) ;
        static void   RANDOMWALK(const gsl_rng *r, void *xp, double step_size) ;
        static double DISTANCE(void* xp, void* yp) ;

        static double evalFunc(void *xp) ;
        static double evalFunc_Multi(void *xp) ;
        static double evalF(std::vector<AdvFuncBase*> funcList, std::vector<Double> srcX, std::vector<Double> ref, 
                                 std::vector<Double> error, std::vector<Double> param, std::vector<Double>& result,
                                 AdvSimulatedAnnealingArgs::CHI2MODE chi2mode);
};

#endif //SIMULATEDANNEALING
