#ifndef AdvOperationBase_h
#define AdvOperationBase_h

#include "Header.hh"
#include "CppToPython.hh"
#include "HeaderBase.hh"
#include "ElementContainer.hh"

#include "AdvMessage.hh"
#include "AdvFuncBase.hh"
#include "AdvFuncParser.hh"
#include "AdvDomain.hh"
#include "AdvParamSet.hh"
#include "AdvMethod.hh"
#include "AdvMethodType.hh"
#include "AdvMethodFactory.hh"

#include "AdvOperationType.hh"

/** 
 *  the base of operations 
 *
 *  @author TANIMORI Souichirou, AdvanceSoft Corp.
 *  @version 0.0
 */
class AdvOperationBase : public AdvMessage {
    /* {since=2.1.14}*/

    private:
        /** the name of the class (for debug) */
        static const std::string className;

 //private:
    //static const std::string DOMAIN; // = std::string("domain")

    private:
        /**  type of opearation */
        AdvOperationType operationType;

    protected:
    
        /**  source data */
        ElementContainer *source;
        /* {set=true, get=false}*/

        /** source data for multiple data fitting */
        //ElmentContainerArray *sourceArray;
    
        /**
         *  the pointer to result element container
         *   @version 0.0
         */
        ElementContainer *result;
        /* {pointer=true}*/

        //ElementContainerArray *resultArray;

        /**  method */
        AdvMethod *method;

        /** parameters for the method*/
        AdvParamSet param;

        /**  domain of the opearion */
        AdvDomain domain;

        /** an array of the opearation */
        //std::vector<AdvDomain> domainArray;

     protected:
        /**
         *  set the type of the opearaion
         *
         *  @param[in] type the type of the opearation
         */
        void setOperationType( AdvOperationType type) { this->operationType=type; };

        /**
         *  set tje type of opearaion
         *
         *  @param[in] type the type of the opearation
         */
        AdvOperationType getOpeartionType() { return this->operationType; } ;


    public:
    
        /**  default constructor */
        AdvOperationBase();

        /**  constructor */
        AdvOperationBase(ElementContainer *src);

        /**  constructor */
#ifndef SWIGPYTHON
        AdvOperationBase(ElementContainer *src,       AdvMethod     *method);
#endif
        AdvOperationBase(ElementContainer *src, const AdvMethodType &methodType);
        AdvOperationBase(ElementContainer *src, const std::string     &methodName);

        /**  constructor */
#ifndef SWIGPYTHON
        AdvOperationBase(ElementContainer *src,       AdvMethod     *method,     const Double xLower, const Double xUpper);
#endif
        AdvOperationBase(ElementContainer *src, const AdvMethodType &methodType, const Double xLower, const Double xUpper);
        AdvOperationBase(ElementContainer *src, const std::string     &methodName, const Double xLower, const Double xUpper);

        /**  constructor */
#ifndef SWIGPYTHON
        AdvOperationBase(ElementContainer *src,       AdvMethod     *method,     const UInt4 lower, const UInt4 upper);
#endif
        AdvOperationBase(ElementContainer *src, const AdvMethodType &methodType, const UInt4 lower, const UInt4 upper);
        AdvOperationBase(ElementContainer *src, const std::string     &methodName, const UInt4 lower, const UInt4 upper);
    
        /**  constructor with determing a AdvDomain for the operation */
        AdvOperationBase(ElementContainer *src, const Double xLower, const Double xUpper);


        /**  destructor */
        virtual ~AdvOperationBase();
    
        /**  set an element container as the source */
        void setSource(ElementContainer *src);

        /**  set a method */
#ifndef SWIGPYTHON
        void setMethod(AdvMethod *method);
#endif

        /**  set a method */
        void setMethod(const AdvMethodType &type);

        /**  set a method */
        void setMethod(const std::string &method);
   
        /**  set member variables by given values */
        void setDomain(ElementContainer *src, const Double lower, const Double upper);
    
        /**  set the domain of the operarion to given parameters */
        void setDomain(const Double xLower, const Double xUpper);

        /**  set the domain of the operarion to given parameters, dprecated */
        void setDomain(const UInt4 lower, const UInt4 upper);

        /**  set the domain of the operation by bin IDs */
        void setDomainByBinID(const UInt4 lower, const UInt4 upper);

        /**  set the domain of the operation by bin bound IDs */
        void setDomainByBinBoundID(const UInt4 lower, const UInt4 upper);

        /**  set the types of the domain bounds for the operarion */
        void setDomain(const AdvDomain::BoundsType type);

        /** get the AdvDomain for the operation */
        AdvDomain getDomain();

        /** get the lower bound of the domain */
        Double getLowerBound();

        /** get the upper bound of the domain */
        Double getUpperBound();

        /** get the index for the upper bound of the domain */
        UInt4  getLowerBoundID();

        /** get the index for the upper bound of the domain */
        UInt4  getUpperBoundID();
    
        /**  set the opration specific paramters */
        void setDefaultParam();

        /**  set the opration specific paramters to the given values */
        void setParam(AdvParamSet param);

        /**  set a boolean paramter with key to the given value */
        void setParam(std::string key, const Bool value);

        /**  set a Int4 type paramter with key to the given value */
        void setParam(std::string key, const Int4 value);

        /**  set a UInt4 type paramter with key to the given value */
        void setParam(std::string key, const UInt4 value);

        /**  set a Double type paramter with key to the given value */
        void setParam(std::string key, const Double value);

        /**  set a Double type std::vector with key */
        void setParam(std::string key, std::vector<Double> value);
#ifndef SWIGPYTHON
#endif

        void setParam(std::string key, PyObject *value);

        /**  set the i-th component of a vector with key to the given value */
        void setParam(std::string key, const UInt4 i, const Double value);

        /**  set a Double type matrix (std::vector< std::vector<Double> >) with key */
        void setParam(std::string key, std::vector< std::vector<Double> > value);

        /**  set the (i, j) component of a matrix with key to the given value */
        void setParam(std::string key, const UInt4 i, const UInt4 j, const Double value);

        /** set a AdvPeakData type parameter woth key */
        void setParam(std::string key, AdvPeakData value);

#ifndef SWIGPYTHON
        /** set a function list */
        void setParam(std::string key, std::vector<AdvFuncBase*> funcList);
#endif
        /** set a function list */
        void setParam(std::string key, std::string expr);

#ifndef SWIGPYTHON
        /** set a function Matrix */
        void setParam(std::string key, std::vector< std::vector<AdvFuncBase*> > funcMatrix);
#endif
        /** set a function Matrix */
        void setParam(std::string key, std::vector<std::string> expr);


        /**  chsek parameters for the given method */
        Bool checkParam();

        /**  get a set of parameters fitted for the method */
        AdvParamSet getParam();

        /**  get a set of fitted parameters */
        AdvParamSet getFittedParam();

        /**  chi squere */
        virtual Double chiSq()  = 0;

        /**  execute the operation */
        virtual void execute() = 0;
    
        /**  return the element container as a results for the operation. */
        virtual ElementContainer getResult();
    
};

#endif // AdvOperationBase_h
