#ifndef FUNCCOMB_H
#define FUNCCOMB_H

#include <stdexcept>

#include "Header.hh"
#include "CppToPython.hh"

#include "AdvMessage.hh"
#include "AdvFuncBase.hh"
#include "AdvFuncParser.hh"

class AdvFuncComb : public AdvMessage {

    private:
        static const std::string className;

    private:
        /** a list of functions */
        std::vector<AdvFuncBase*> funcList;
        /** a list of values of paramters for each functions */
        std::vector< std::vector<Double> > paramArray;
        /** a list of values of paramter errors for each functions */
        std::vector< std::vector<Double> > errorArray;

        std::vector<Double> linkList;

    private:
        void setFuncList(const std::vector<AdvFuncBase*> funcList) ;
        void setFuncList(const std::string                expr);
        void setLinkList(const std::vector<Double> linkList);
        //void setParamList(                                     );
        void setParamList(const std::vector<Double>       &paramList);
        void setParamList(PyObject                   *paramList);
        void setErrorList(                                     );
        void setErrorList(const std::vector<Double>       &errorList);
        void setErrorList(PyObject                      *PyList);
        void setErrorList(const std::vector< std::vector<Double> > &covar);

        Bool checkNumberOfParam(const std::vector<Double> &list) const ;
        Bool checkNumberOfParam(const std::vector< std::vector<Double> > &covar) const ;
        std::vector< std::vector<Double> > vectorToArray(const std::vector<Double> &list);

        /** inner product of two vectors */
        Double innerProduct(const std::vector<Double> &x, const std::vector<Double> &y);

    public:
        AdvFuncComb();
        /* */
        AdvFuncComb(const std::vector<AdvFuncBase*> funcList);

        AdvFuncComb(const std::vector<AdvFuncBase*> funcList,const std::string lk, const std::vector<Double>);

        AdvFuncComb(const std::string            expr);

        /** constructor */
        AdvFuncComb(const std::vector<AdvFuncBase*> fList, const std::vector<Double> param);

        //AdvFuncComb(const std::vector<AdvFuncBase*> fList, const std::vector<Double> param);
        AdvFuncComb(const std::string             expr, PyObject            *param);
        /** constructor */
        AdvFuncComb(const std::vector<AdvFuncBase*> fList, const std::vector<Double> param, const std::vector< std::vector<Double> > &covar);
        /** constructor */
        AdvFuncComb(const std::vector<AdvFuncBase*> fList, const std::vector<Double> param, const std::vector<Double> errorList);
        AdvFuncComb(const std::string            expr,  PyObject            *param, PyObject            *errorList);
        /** destructor */
        ~AdvFuncComb();

       void set(const std::vector<AdvFuncBase*> funcList, const std::vector<Double> paramList);
       //void set(const std::string expr, PyObject *list);
   
       void set(const std::vector<AdvFuncBase*> funcList, const std::vector<Double> &paramList, const std::vector< std::vector<Double> > &covar);
       void set(const std::string expr, PyObject *paramList, PyObject *errorList);
       void set(const std::vector<AdvFuncBase*> funcList, const std::vector<Double> &paramList, const std::vector<Double> &errorList);

       void set(const std::vector<Double> &p                         ) ;
       void set(const std::vector<Double> &p, const std::vector<Double> &e) ;
       void set(PyObject             *p                         ) ;
       void set(PyObject             *p, PyObject             *e) ;
       void setError(const std::vector<Double> &e) ;
       void setError(PyObject             *e) ;

       /** add a function and its parameters */
       void add(AdvFuncBase *func, const std::vector<Double> &paramList);
       /** add a function, its parameters and paremter errors */
       void add(AdvFuncBase *func, const std::vector<Double> &paramList, const std::vector<Double> &errorList);
       /** erace the i-th. component */
       void erase(UInt4 i);

       /** dump function components */
       void dump();

       /** get the number of function components */
       UInt4 getNumberOfComponents() const ;
       /** get the name of the i-th. function component */
       std::string getName(UInt4 i) const ;
       /** get the symbol of the i-th. function component */
       std::string getSymbol(UInt4 i) const ;
       /** get the number of required parameters */
       UInt4 getNumberOfRequiredParam() const ;
       /** get the number of required parameters for the i-th. function component */
       UInt4 getNumberOfRequiredParam(UInt4 i) const ;

       /** get parameters for i-th. component of the function */
       std::vector<Double> getSubsequenceOfParam(UInt4 i);
       /** get parameter errors for i-th. component of the function */
       std::vector<Double> getSubsequenceOfParamError(UInt4 i);

       /** evaluate the error value of the function at the given point */
       Double evalComponent(const Double x, const UInt4 i);
#ifdef HAVE_DIFFERENTIAL_MEMBER
       /** evaluage the 1st derivative coefficient of the function component at the given point */
       Double der1stComponent(const Double x, const UInt4 i);
       /** evaluage the 2nd derivative coefficient of the function component at the given point */
       Double der2ndComponent(const Double x, const UInt4 i);
       /** evaluate the gradient of the function component from parameters at the given point */
       std::vector<Double> gradientComponent(const Double x, const UInt4 i);
       /** evaluate the error of the function component from parameters at the given point */
       Double evalErrorComponent(const Double x, const UInt4 i);
#endif // HAVE_DIFFERENTIAL_MEMBER

       /** evaluate the value of the function at the given point */
       Double eval(const Double x);
#ifdef HAVE_DIFFERENTIAL_MEMBER
       Double der1st(const Double x);
       Double der2nd(const Double x);
       std::vector<Double> gradient(const Double x);
       /** evaluate the error value of the function from parameters at the given point */
       Double evalError(const Double x);
#endif // HAVE_DIFFERENTIAL_MEMBER

       /** evaluate the lisf of the error value for the function at the given points */
       std::vector<Double> evalComponent(const std::vector<Double> &x, const UInt4 i);
#ifdef HAVE_DIFFERENTIAL_MEMBER
       std::vector<Double> der1stComponent(const std::vector<Double> &x, const UInt4 i);
       std::vector<Double> der2ndComponent(const std::vector<Double> &x, const UInt4 i);
       std::vector< std::vector<Double> > gradientComponent(const std::vector<Double> &x, const UInt4 i);
       /** evaluate the list of the error of the function component from parameters at the given points*/
       std::vector<Double> evalErrorComponent(const std::vector<Double> &x, UInt4 i);
#endif // HAVE_DIFFERENTIAL_MEMBER


       /** evaluate the list of the function's values at the given points */
       std::vector<Double> eval(const std::vector<Double> &x);
#ifdef HAVE_DIFFERENTIAL_MEMBER
       std::vector<Double> der1st(const std::vector<Double> &x);
       std::vector<Double> der2nd(const std::vector<Double> &x);
       std::vector< std::vector<Double> > gradient(const std::vector<Double> &x);
       /** evaluate the list of the error for the function from parameters at the given points */
       std::vector<Double> evalError(const std::vector<Double> &x);
#endif // HAVE_DIFFERENTIAL_MEMBER

};
#endif
